@kreiseck/kasseneck-api 1.0.0 → 1.1.1

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 (85) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/cjs/pos/texte.d.ts +51 -0
  3. package/dist/cjs/pos/texte.js +32 -0
  4. package/dist/cjs/printing/code-table-preview.d.ts +15 -0
  5. package/dist/cjs/printing/code-table-preview.js +24 -0
  6. package/dist/cjs/printing/code-tables.d.ts +77 -0
  7. package/dist/cjs/printing/code-tables.js +224 -0
  8. package/dist/cjs/printing/escpos.d.ts +19 -6
  9. package/dist/cjs/printing/escpos.js +27 -48
  10. package/dist/cjs/printing/index.d.ts +2 -0
  11. package/dist/cjs/printing/index.js +8 -1
  12. package/dist/cjs/printing/printable.d.ts +14 -2
  13. package/dist/cjs/printing/printable.js +36 -2
  14. package/dist/cjs/react/index.js +7 -0
  15. package/dist/cjs/receipt/blatt.d.ts +9 -0
  16. package/dist/cjs/receipt/code-table-reference-data.d.ts +6 -0
  17. package/dist/cjs/receipt/code-table-reference-data.js +11 -0
  18. package/dist/cjs/receipt/code-table-test-sheet.d.ts +58 -0
  19. package/dist/cjs/receipt/code-table-test-sheet.js +155 -0
  20. package/dist/cjs/receipt/index.d.ts +1 -0
  21. package/dist/cjs/receipt/index.js +6 -1
  22. package/dist/cjs/receipt/layout-escpos.d.ts +6 -1
  23. package/dist/cjs/receipt/layout-escpos.js +11 -7
  24. package/dist/cjs/version.d.ts +1 -1
  25. package/dist/cjs/version.js +1 -1
  26. package/dist/esm/pos/texte.d.ts +51 -0
  27. package/dist/esm/pos/texte.js +32 -0
  28. package/dist/esm/printing/code-table-preview.d.ts +15 -0
  29. package/dist/esm/printing/code-table-preview.js +21 -0
  30. package/dist/esm/printing/code-tables.d.ts +77 -0
  31. package/dist/esm/printing/code-tables.js +215 -0
  32. package/dist/esm/printing/escpos.d.ts +19 -6
  33. package/dist/esm/printing/escpos.js +27 -48
  34. package/dist/esm/printing/index.d.ts +2 -0
  35. package/dist/esm/printing/index.js +2 -0
  36. package/dist/esm/printing/printable.d.ts +14 -2
  37. package/dist/esm/printing/printable.js +36 -2
  38. package/dist/esm/react/index.js +7 -0
  39. package/dist/esm/receipt/blatt.d.ts +9 -0
  40. package/dist/esm/receipt/code-table-reference-data.d.ts +6 -0
  41. package/dist/esm/receipt/code-table-reference-data.js +8 -0
  42. package/dist/esm/receipt/code-table-test-sheet.d.ts +58 -0
  43. package/dist/esm/receipt/code-table-test-sheet.js +149 -0
  44. package/dist/esm/receipt/index.d.ts +1 -0
  45. package/dist/esm/receipt/index.js +1 -0
  46. package/dist/esm/receipt/layout-escpos.d.ts +6 -1
  47. package/dist/esm/receipt/layout-escpos.js +11 -7
  48. package/dist/esm/version.d.ts +1 -1
  49. package/dist/esm/version.js +1 -1
  50. package/fixtures/code-table-preview.json +14 -0
  51. package/fixtures/code-table-receipts.json +84 -0
  52. package/fixtures/code-tables.json +12 -0
  53. package/fixtures/expected/code-table-receipt.long-item-name.iso8859_15.hex +27 -0
  54. package/fixtures/expected/code-table-receipt.long-item-name.pc437.hex +27 -0
  55. package/fixtures/expected/code-table-receipt.long-item-name.pc850.hex +27 -0
  56. package/fixtures/expected/code-table-receipt.long-item-name.pc858.hex +27 -0
  57. package/fixtures/expected/code-table-receipt.long-item-name.replacement.hex +27 -0
  58. package/fixtures/expected/code-table-receipt.long-item-name.wpc1252.hex +27 -0
  59. package/fixtures/expected/code-table-receipt.sale-cash.iso8859_15.hex +34 -0
  60. package/fixtures/expected/code-table-receipt.sale-cash.pc437.hex +34 -0
  61. package/fixtures/expected/code-table-receipt.sale-cash.pc850.hex +34 -0
  62. package/fixtures/expected/code-table-receipt.sale-cash.pc858.hex +34 -0
  63. package/fixtures/expected/code-table-receipt.sale-cash.replacement.hex +34 -0
  64. package/fixtures/expected/code-table-receipt.sale-cash.wpc1252.hex +34 -0
  65. package/fixtures/expected/code-table-receipt.special-characters.iso8859_15.hex +30 -0
  66. package/fixtures/expected/code-table-receipt.special-characters.pc437.hex +30 -0
  67. package/fixtures/expected/code-table-receipt.special-characters.pc850.hex +30 -0
  68. package/fixtures/expected/code-table-receipt.special-characters.pc858.hex +30 -0
  69. package/fixtures/expected/code-table-receipt.special-characters.replacement.hex +30 -0
  70. package/fixtures/expected/code-table-receipt.special-characters.wpc1252.hex +30 -0
  71. package/fixtures/expected/code-table-receipt.test-cashregister-sale.iso8859_15.hex +44 -0
  72. package/fixtures/expected/code-table-receipt.test-cashregister-sale.pc437.hex +44 -0
  73. package/fixtures/expected/code-table-receipt.test-cashregister-sale.pc850.hex +44 -0
  74. package/fixtures/expected/code-table-receipt.test-cashregister-sale.pc858.hex +44 -0
  75. package/fixtures/expected/code-table-receipt.test-cashregister-sale.replacement.hex +44 -0
  76. package/fixtures/expected/code-table-receipt.test-cashregister-sale.wpc1252.hex +44 -0
  77. package/fixtures/expected/code-table-test-sheet.lines.json +209 -0
  78. package/fixtures/expected/code-table-test-sheet.mm58.hex +62 -0
  79. package/fixtures/expected/code-table-test-sheet.mm80.hex +63 -0
  80. package/fixtures/hobex-hps-codes.json +1 -1
  81. package/fixtures/invoice-api.schema.json +1 -1
  82. package/fixtures/invoice-texts.json +1 -1
  83. package/fixtures/pos-texts.json +58 -1
  84. package/fixtures/surface.json +1 -1
  85. package/package.json +4 -2
@@ -15,6 +15,7 @@
15
15
  * ersatzlos weg, alles Uebrige auf `?`. Ein Artikelname aus einer
16
16
  * ERP-Uebernahme mit Gedankenstrich soll den Beleg nicht verhindern.
17
17
  */
18
+ import { codeTableById, ersatzBuchstaben } from './code-tables.js';
18
19
  /** Ersetzungen aus print_paper.dart, Schluessel sind Unicode-Codepunkte. */
19
20
  const ERSETZUNGEN = new Map([
20
21
  [0x2013, '-'], // – Halbgeviertstrich
@@ -63,12 +64,34 @@ function istEmojiOderNullbreite(codepunkt) {
63
64
  * Vor `escPosText`/`escPosRow` anwenden, wenn der Text aus fremder Hand kommt
64
65
  * (Artikelstamm, ERP-Uebernahme, Kundenname). `•` wird hier zu `*`, und der
65
66
  * Erzeuger sagt dasselbe — auch wer diese Funktion weglaesst, bekommt also
66
- * dasselbe Zeichen (siehe ZEICHEN_ERSATZ in escpos.ts).
67
+ * dasselbe Zeichen (siehe ZEICHEN_ERSATZ in code-tables.ts).
68
+ *
69
+ * Mit `codeTable` wird der Text fuer genau diese Tabelle fertig gemacht:
70
+ * - eine Tabelle des Katalogs (`CodeTableId`): `€` bleibt stehen, wenn die
71
+ * Tabelle es hat (der Drucker bekommt das echte Byte), sonst `EUR`; jedes
72
+ * der zehn Zeichen, das der Tabelle fehlt (`§` auf pc437, alle Umlaute auf
73
+ * `replacement` ...), wird schon hier zu seinen Ersatzbuchstaben. Danach
74
+ * ist jedes Zeichen genau ein Byte, und Spalten, die am Text gerechnet
75
+ * werden, stimmen auch am Papier.
76
+ * - `CP437` (alter Name): wie `pc437`, also `§` -> `Par.`.
77
+ * - ohne Angabe, `null` oder `CP1252`: genau wie bisher (Bons ohne gewaehlte
78
+ * Tabelle bleiben byte-gleich).
67
79
  */
68
- export function escPosPrintableText(text) {
80
+ export function escPosPrintableText(text, codeTable) {
81
+ const tabelle = tabelleFuerErsatz(codeTable);
82
+ const fehlend = tabelle === null ? [] : codeTableById(tabelle).missing;
83
+ const echtesEuro = tabelle !== null && !fehlend.includes('€');
69
84
  let ergebnis = '';
70
85
  for (const zeichen of text) {
71
86
  const codepunkt = zeichen.codePointAt(0);
87
+ if (fehlend.includes(zeichen)) {
88
+ ergebnis += ersatzBuchstaben(zeichen);
89
+ continue;
90
+ }
91
+ if (codepunkt === 0x20ac && echtesEuro) {
92
+ ergebnis += zeichen;
93
+ continue;
94
+ }
72
95
  const ersatz = ERSETZUNGEN.get(codepunkt);
73
96
  if (ersatz !== undefined) {
74
97
  ergebnis += ersatz;
@@ -85,3 +108,14 @@ export function escPosPrintableText(text) {
85
108
  }
86
109
  return ergebnis;
87
110
  }
111
+ /**
112
+ * Tabelle, deren fehlende Zeichen schon am Text ersetzt werden. `null` heisst
113
+ * alter Weg ohne Aenderung (keine Wahl, `null`, `CP1252`).
114
+ */
115
+ function tabelleFuerErsatz(codeTable) {
116
+ if (codeTable === undefined || codeTable === null || codeTable === 'CP1252')
117
+ return null;
118
+ if (codeTable === 'CP437')
119
+ return 'pc437';
120
+ return codeTable;
121
+ }
@@ -90,6 +90,13 @@ export function ReceiptSheetLines({ sheet: blatt, logoUrl, renderQr, qrHidden: q
90
90
  return (_jsx("div", { className: klasse, "data-zeichen": z, style: blattStil, children: blatt.blocks.map((b, i) => {
91
91
  switch (b.kind) {
92
92
  case 'line':
93
+ if (b.doubleSizeLead !== undefined && b.doubleSizeLead > 0) {
94
+ // Wie am Papier (`GS !` doppelt): die Nummernzelle in doppelter
95
+ // Schrift, die Zeile zwei Zeilen hoch, der Rest auf der Grundlinie.
96
+ // `ch` im Span misst die doppelte Schrift, darum die halbe Zahl.
97
+ const n = b.doubleSizeLead;
98
+ return (_jsxs("div", { className: "keck-blatt-zeile keck-blatt-zeile--gross", style: { display: 'flex', alignItems: 'flex-end', whiteSpace: 'pre', height: '4ch', overflow: 'hidden', fontWeight: b.bold ? 'bold' : 'normal' }, children: [_jsx("span", { style: { fontSize: '2em', width: `${n / 2}ch`, lineHeight: '2ch', fontWeight: 'bold', textAlign: 'center', flex: 'none' }, children: b.text.slice(0, n).trim() }), _jsx("span", { style: { lineHeight: '2ch' }, children: b.text.slice(n) })] }, i));
99
+ }
93
100
  return (_jsx("div", { className: "keck-blatt-zeile", style: { whiteSpace: 'pre', height: '2ch', lineHeight: '2ch', overflow: 'hidden', fontWeight: b.bold ? 'bold' : 'normal' }, children: b.text }, i));
94
101
  case 'logo':
95
102
  return (_jsx("div", { className: "keck-blatt-logo", style: { height: `${b.heightLines * 2}ch`, display: 'flex', justifyContent: 'center' }, children: logoUrl === undefined ? null : (_jsx("img", { src: logoUrl, alt: "", "aria-hidden": "true", style: { width: `${b.widthFraction * z}ch`, height: `${b.heightLines * 2}ch`, objectFit: 'contain' } })) }, i));
@@ -40,6 +40,15 @@ export type SheetBlock = {
40
40
  readonly text: string;
41
41
  readonly bold: boolean;
42
42
  readonly blank: boolean;
43
+ /**
44
+ * Die ersten n Spalten sind EINE Zelle, deren Inhalt (ohne Leerraum)
45
+ * doppelt breit und doppelt hoch steht -- die Zeile ist dann zwei
46
+ * Zeilen hoch. Nur das Testblatt des Zeichensatzes setzt das (Nummern
47
+ * links); fehlt es, ist alles einfach gross. Ein Zeichner, der das Feld
48
+ * nicht kennt, zeigt die Zeile in einfacher Groesse, die Spalten
49
+ * bleiben dieselben.
50
+ */
51
+ readonly doubleSizeLead?: number;
43
52
  } | {
44
53
  readonly kind: 'logo';
45
54
  readonly widthFraction: number;
@@ -0,0 +1,6 @@
1
+ /** Die Vorlage-Zeile als Rasterbild: 384 x 24 Punkte, MSB zuerst, je Zeile auf volle Bytes aufgefuellt; base64. */
2
+ export declare const CODE_TABLE_REFERENCE_RASTER: {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ readonly bits: string;
6
+ };
@@ -0,0 +1,8 @@
1
+ // Erzeugt von scripts/zeichensatz-vorlage.py -- nicht von Hand aendern.
2
+ // Schrift: Menlo Regular 20 px (sha256 dc256e0b39c2a6fec947129d421fef41b8b429f58f9b6e5d1b148c87f775c1f6), Schwelle 160.
3
+ /** Die Vorlage-Zeile als Rasterbild: 384 x 24 Punkte, MSB zuerst, je Zeile auf volle Bytes aufgefuellt; base64. */
4
+ export const CODE_TABLE_REFERENCE_RASTER = {
5
+ width: 384,
6
+ height: 24,
7
+ bits: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYAAGYAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYAAGYAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYAAGYAAGYAABgAADwAAIEAAH4AAB8AAD4AADwAAAAAAAAAAAAAAAAAAAAAAAAAAGYAAGYAAGYAADwAAH4AAcOAAOcAAD8AAH4AAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAMMAAcOAAMMAAHAAAGAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAMMAAcOAAMMAAOAAAGAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAH4AAMMAAGYAAcOAAcOAAMwAAMAAAHAAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAOcAAOcAAMMAAGYAAYGAAcOAANgAAfwAAHgAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAMMAAMMAAGYAAYGAAcOAANgAAfwAAM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAcOAAMMAAGcAAYGAAcOAANwAAMAAAMcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAYGAAMMAAMMAAYGAAcOAAM4AAfgAAMMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAYGAAMMAAP8AAYGAAcOAAMcAA/gAAOMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcMAAYGAAMMAAP8AAcGAAcOAAMGAAMAAAHsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYMAAcOAAMMAAcOAAcOAAcOAAMGAAOAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYMAAMMAAMMAAYGAAMMAAMMAAMGAAGAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO8AAOcAAP8AAYGAAP8AAP8AAPOAAH8AAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAH4AAH8AA4HAAH4AAH4AAP8AAD8AAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
8
+ };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Das Testblatt fuer den Zeichensatz: welche Code-Tabelle druckt auf diesem
3
+ * Drucker die Umlaute richtig?
4
+ *
5
+ * Oben steht die Vorlage-Zeile als Rasterbild -- ein Bild druckt jeder
6
+ * Drucker gleich, egal welche Tabelle er gerade hat. Darunter je Tabelle des
7
+ * Katalogs eine Zeile mit ihrer Nummer (gross), umgeschaltet mit `ESC t n`
8
+ * und mit den Bytes dieser Tabelle. Richtig ist die Zeile ohne falsches
9
+ * Zeichen; eine Luecke (Zeichen fehlt der Tabelle) ist in Ordnung, bei
10
+ * mehreren die mit den wenigsten Luecken, sonst Zeile 6 (Ersatzbuchstaben).
11
+ * Nicht „die erste Zeile mit richtigen Umlauten“: ein Drucker mit nur PC437
12
+ * druckt Zeile 2 und 3 mit richtigen Umlauten, aber falschem € und §.
13
+ *
14
+ * Alles ausserhalb der Testzeilen steht mit Ersatzbuchstaben (reines ASCII),
15
+ * auch die Anleitung: „Lücke“ aus dem Katalog steht am Blatt als „Luecke“.
16
+ *
17
+ * Bildschirm und Papier zeigen dasselbe Blatt: `codeTableTestSheet` liefert
18
+ * es im Zeilenmodell des Beleg-Blatts (`ReceiptSheet`), `codeTableTestSheetBytes`
19
+ * druckt genau diese Zeilen. Das Blatt hat immer 32 Spalten (58 mm); auf
20
+ * 80 mm steht der Block ueber den linken Rand (`GS L`) mittig.
21
+ *
22
+ * Gemeinsamer Prueffall mit dem Dart-Zwilling:
23
+ * `fixtures/expected/code-table-test-sheet.{mm58,mm80}.hex` und `.lines.json`.
24
+ */
25
+ import type { ReceiptSheet } from './blatt.js';
26
+ import { type CodeTable, type CodeTableId } from '../printing/code-tables.js';
27
+ import { type PosPaperSize, type RasterImage } from '../printing/escpos.js';
28
+ /** Spalten des Testblatts -- immer 58-mm-Format. */
29
+ export declare const CODE_TABLE_TEST_SHEET_CHARS = 32;
30
+ export interface CodeTableTestSheetInput {
31
+ /** Kassen-Kennung fuer die Kopfzeile (keine Personendaten). */
32
+ cashregisterLabel: string;
33
+ /** Zeitpunkt des Drucks; die Kopfzeile zeigt ihn in Wiener Zeit. */
34
+ time: Date;
35
+ /** Papier des Druckers; das Blatt bleibt 32 Spalten, auf 80 mm mittig. */
36
+ paper: PosPaperSize;
37
+ }
38
+ /** Eine Testzeile: Nummer, Tabelle und die Zeichen, die ihr fehlen (fuer „ohne €“ am Bildschirm). */
39
+ export interface CodeTableTestSheetRow {
40
+ readonly number: CodeTable['number'];
41
+ readonly codeTable: CodeTableId;
42
+ readonly missing: readonly string[];
43
+ }
44
+ /** Das Testblatt im Zeilenmodell des Beleg-Blatts, dazu die Testzeilen. */
45
+ export interface CodeTableTestSheet extends ReceiptSheet {
46
+ readonly rows: readonly CodeTableTestSheetRow[];
47
+ }
48
+ /** Die Vorlage-Zeile als Rasterbild (384 x 24 Punkte), wie sie am Papier steht. */
49
+ export declare function codeTableReferenceImage(): RasterImage;
50
+ /** Das Testblatt im Zeilenmodell des Beleg-Blatts -- dieselben Zeilen wie am Papier. */
51
+ export declare function codeTableTestSheet(options: CodeTableTestSheetInput): CodeTableTestSheet;
52
+ /**
53
+ * Das Testblatt als ESC/POS-Bytes. Jede Testzeile schaltet nach ihrer
54
+ * Nummer einmal mit `FS .` + `ESC t n` auf ihre Tabelle und traegt deren Bytes (`encodeForCodeTable`,
55
+ * also das echte €-Byte, wo die Tabelle es hat). Die Nummer steht doppelt
56
+ * breit und hoch (`GS !`), die Vorlage als Rasterbild (`GS v 0`).
57
+ */
58
+ export declare function codeTableTestSheetBytes(options: CodeTableTestSheetInput): Uint8Array;
@@ -0,0 +1,149 @@
1
+ import { entpackeRasterBits } from './marke.js';
2
+ import { toViennaWallClock } from '../vienna-time.js';
3
+ import { labelText, messageText } from '../pos/texte.js';
4
+ import { CODE_TABLES, encodeForCodeTable } from '../printing/code-tables.js';
5
+ import { CODE_TABLE_REFERENCE_RASTER } from './code-table-reference-data.js';
6
+ import { createEscPosDocument, escPosBytes, escPosCut, escPosRasterImage, escPosReset, escPosSetStyles, escPosText, wrapText, } from '../printing/escpos.js';
7
+ /** Spalten des Testblatts -- immer 58-mm-Format. */
8
+ export const CODE_TABLE_TEST_SHEET_CHARS = 32;
9
+ /** Die zehn Zeichen der Vorlage, in der Reihenfolge des Katalogs. */
10
+ const VORLAGE = ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü', 'ß', '€', '§', '°'];
11
+ /** Spalte des ersten Zeichens; danach jede zweite. Muss zu scripts/zeichensatz-vorlage.py passen. */
12
+ const ERSTE_SPALTE = 5;
13
+ /** Breite der Nummernzelle: eine Ziffer doppelt breit. */
14
+ const NUMMER_SPALTEN = 2;
15
+ const TRENNER = ' | ';
16
+ /** Punkte je Blatt-Zeile: 32 Zeichen zu 12 Punkten. */
17
+ const BLATT_PUNKTE = CODE_TABLE_TEST_SHEET_CHARS * 12;
18
+ /** Linker Rand, der den 32-Spalten-Block auf 80 mm (576 Punkte) mittig setzt. */
19
+ const RAND_80 = (576 - BLATT_PUNKTE) / 2;
20
+ const GS = 0x1d;
21
+ const ESC = 0x1b;
22
+ /** `ESC t` der Vorgabe ohne gewaehlte Tabelle (WPC1252). */
23
+ const VORGABE_ESC_T = 16;
24
+ /** Die Vorlage-Zeile als Rasterbild (384 x 24 Punkte), wie sie am Papier steht. */
25
+ export function codeTableReferenceImage() {
26
+ const r = CODE_TABLE_REFERENCE_RASTER;
27
+ return entpackeRasterBits(r.bits, r.width, r.height);
28
+ }
29
+ /** Text in reines ASCII (Ersatzbuchstaben): steht auf jedem Drucker gleich, egal welche Tabelle gilt. */
30
+ function nurAscii(text) {
31
+ return String.fromCharCode(...encodeForCodeTable(text, 'replacement'));
32
+ }
33
+ function zeile(text, bold = false, ausrichtung = 'left') {
34
+ const rest = CODE_TABLE_TEST_SHEET_CHARS - text.length;
35
+ const links = ausrichtung === 'center' ? Math.floor(rest / 2) : 0;
36
+ return { kind: 'line', text: ' '.repeat(links) + text + ' '.repeat(rest - links), bold, blank: false };
37
+ }
38
+ function zeilen(text, bold = false, ausrichtung = 'left') {
39
+ return wrapText(nurAscii(text), CODE_TABLE_TEST_SHEET_CHARS).map((t) => zeile(t, bold, ausrichtung));
40
+ }
41
+ /** Die Zeichen einer Tabelle an ihren Spalten; ein fehlendes Zeichen laesst seine Luecke. */
42
+ function zeichenDerTabelle(tabelle) {
43
+ return VORLAGE.map((z) => (tabelle.missing.includes(z) ? ' ' : z)).join(' ').trimEnd();
44
+ }
45
+ function nummernZeile(nummer, text) {
46
+ const kopf = String(nummer).padStart(NUMMER_SPALTEN, ' ') + TRENNER;
47
+ return { ...zeile(kopf + text), doubleSizeLead: NUMMER_SPALTEN };
48
+ }
49
+ const zweistellig = (n) => String(n).padStart(2, '0');
50
+ /** Das Testblatt im Zeilenmodell des Beleg-Blatts -- dieselben Zeilen wie am Papier. */
51
+ export function codeTableTestSheet(options) {
52
+ return blattBauen(options).blatt;
53
+ }
54
+ /**
55
+ * Baut das Blatt und merkt sich dabei, welcher Block die Vorlage-Zeile ist.
56
+ * Die Bytes suchen sie nicht am Text (der ist `nurAscii` und kann sich
57
+ * aendern), sondern nehmen genau diese Stelle.
58
+ */
59
+ function blattBauen(options) {
60
+ const z = CODE_TABLE_TEST_SHEET_CHARS;
61
+ const uhr = toViennaWallClock(options.time);
62
+ const zeit = `${zweistellig(uhr.day)}.${zweistellig(uhr.month)}. ${zweistellig(uhr.hour)}:${zweistellig(uhr.minute)}`;
63
+ const doppelt = zeile('='.repeat(z));
64
+ const einfach = zeile('-'.repeat(z));
65
+ const einzug = ' '.repeat(ERSTE_SPALTE);
66
+ const bloecke = [
67
+ doppelt,
68
+ ...zeilen(labelText('codetable.title'), true, 'center'),
69
+ ...zeilen(`${options.cashregisterLabel.trim()} ${zeit}`, false, 'center'),
70
+ doppelt,
71
+ ...zeilen(labelText('codetable.reference')),
72
+ ];
73
+ // Am Papier ein Bild (`codeTableReferenceImage`), am Bildschirm Text.
74
+ const vorlage = bloecke.length;
75
+ bloecke.push(zeile(einzug + VORLAGE.join(' ')), einfach);
76
+ const ersatz = CODE_TABLES.find((t) => t.id === 'replacement');
77
+ for (const t of CODE_TABLES) {
78
+ if (t === ersatz)
79
+ continue;
80
+ bloecke.push(nummernZeile(t.number, zeichenDerTabelle(t)));
81
+ }
82
+ bloecke.push(einfach);
83
+ const ersatzText = String.fromCharCode(...encodeForCodeTable(VORLAGE.join(' '), 'replacement'));
84
+ const [ersteErsatz = '', ...restErsatz] = wrapText(ersatzText, z - ERSTE_SPALTE);
85
+ bloecke.push(nummernZeile(ersatz.number, ersteErsatz));
86
+ for (const t of restErsatz)
87
+ bloecke.push(zeile(einzug + t));
88
+ bloecke.push(zeile(einzug + nurAscii(labelText('codetable.replacement_note'))));
89
+ bloecke.push(doppelt);
90
+ bloecke.push(...zeilen(labelText('codetable.instruction_title'), true));
91
+ bloecke.push(...zeilen(messageText('codetable.instruction')));
92
+ bloecke.push(...zeilen(messageText('codetable.instruction_none', { number: ersatz.number })));
93
+ bloecke.push(doppelt);
94
+ return {
95
+ blatt: {
96
+ charsPerLine: z,
97
+ blocks: bloecke,
98
+ rows: CODE_TABLES.map((t) => ({ number: t.number, codeTable: t.id, missing: t.missing })),
99
+ },
100
+ vorlage,
101
+ };
102
+ }
103
+ /**
104
+ * Das Testblatt als ESC/POS-Bytes. Jede Testzeile schaltet nach ihrer
105
+ * Nummer einmal mit `FS .` + `ESC t n` auf ihre Tabelle und traegt deren Bytes (`encodeForCodeTable`,
106
+ * also das echte €-Byte, wo die Tabelle es hat). Die Nummer steht doppelt
107
+ * breit und hoch (`GS !`), die Vorlage als Rasterbild (`GS v 0`).
108
+ */
109
+ export function codeTableTestSheetBytes(options) {
110
+ const { blatt, vorlage: vorlageIndex } = blattBauen(options);
111
+ const vorlage = blatt.blocks[vorlageIndex];
112
+ if (vorlage === undefined || vorlage.kind !== 'line')
113
+ throw new Error(`Testblatt: Vorlage-Zeile fehlt (Block ${vorlageIndex})`);
114
+ // Das Dokument ist immer 58 mm: 32 Spalten, Druckbereich 384 Punkte.
115
+ const doc = createEscPosDocument({ paperSize: 'mm58', codeTable: null });
116
+ escPosReset(doc);
117
+ if (options.paper === 'mm80')
118
+ doc.bytes.push(GS, 0x4c, RAND_80 & 0xff, RAND_80 >> 8);
119
+ let tabellen = 0;
120
+ for (const block of blatt.blocks) {
121
+ if (block.kind !== 'line')
122
+ continue;
123
+ if (block === vorlage) {
124
+ escPosRasterImage(doc, codeTableReferenceImage(), { align: 'left' });
125
+ continue;
126
+ }
127
+ if (block.doubleSizeLead === undefined) {
128
+ escPosText(doc, block.text.trimEnd(), { styles: { align: 'left', bold: block.bold } });
129
+ continue;
130
+ }
131
+ const tabelle = blatt.rows[tabellen];
132
+ tabellen += 1;
133
+ const nummer = block.text.slice(0, block.doubleSizeLead).trim();
134
+ // Die Ziffer ist ASCII und steht in jeder Tabelle gleich: keine Umschaltung
135
+ // davor, die Zeile schaltet genau einmal, vor ihren eigenen Bytes.
136
+ escPosSetStyles(doc, { align: 'left', bold: true, width: 2, height: 2 });
137
+ doc.bytes.push(...encodeForCodeTable(nummer, 'replacement'));
138
+ escPosSetStyles(doc, { align: 'left', codeTable: tabelle.codeTable });
139
+ doc.bytes.push(...encodeForCodeTable(block.text.slice(block.doubleSizeLead).trimEnd(), tabelle.codeTable), 0x0a);
140
+ }
141
+ // Endzustand wie ohne Wahl: Tabelle 16 (WPC1252) und auf 80 mm Rand 0.
142
+ // Ein Druck danach ohne `ESC @` steht sonst in der Tabelle der letzten
143
+ // Testzeile und um den Rand verschoben.
144
+ doc.bytes.push(ESC, 0x74, VORGABE_ESC_T);
145
+ if (options.paper === 'mm80')
146
+ doc.bytes.push(GS, 0x4c, 0, 0);
147
+ escPosCut(doc, 'full');
148
+ return escPosBytes(doc);
149
+ }
@@ -6,4 +6,5 @@ export { type SheetLogoSize, type SheetLogo, type LogoDimensions, type SheetBloc
6
6
  export { rasterizeLogo } from './bild.js';
7
7
  export { type BrandMarkRaster, BRAND_MARK_RASTERS, BRAND_MARK_PATHS } from './marke-daten.js';
8
8
  export { brandMarkImage } from './marke.js';
9
+ export { type CodeTableTestSheetInput, type CodeTableTestSheetRow, type CodeTableTestSheet, CODE_TABLE_TEST_SHEET_CHARS, codeTableTestSheet, codeTableTestSheetBytes, codeTableReferenceImage, } from './code-table-test-sheet.js';
9
10
  export { type ReceiptDueTip, type ReceiptDueTipRecipient, type ReceiptDueOptions, type ReceiptDueBuckets, type ReceiptDueBreakdown, type ReceiptDueErrorReason, receiptDueCents, receiptDueBreakdown, ReceiptDueError, RECEIPT_DUE_ERROR_REASONS, isReceiptDueError, } from './due.js';
@@ -6,4 +6,5 @@ export { SHEET_LOGO_SIZES, LOGO_MAX_PIXELS, DOTS_PER_CHAR, DOTS_PER_LINE, paperS
6
6
  export { rasterizeLogo } from './bild.js';
7
7
  export { BRAND_MARK_RASTERS, BRAND_MARK_PATHS } from './marke-daten.js';
8
8
  export { brandMarkImage } from './marke.js';
9
+ export { CODE_TABLE_TEST_SHEET_CHARS, codeTableTestSheet, codeTableTestSheetBytes, codeTableReferenceImage, } from './code-table-test-sheet.js';
9
10
  export { receiptDueCents, receiptDueBreakdown, ReceiptDueError, RECEIPT_DUE_ERROR_REASONS, isReceiptDueError, } from './due.js';
@@ -55,7 +55,12 @@ export declare function blattFuerDruck(layout: ReceiptLayout, optionen: {
55
55
  export interface EscPosLayoutOptions {
56
56
  /** Papierbreite; Vorgabe ist die des Layouts (dessen Spaltenbreiten daran haengen). */
57
57
  paperSize?: PosPaperSize;
58
- /** Codepage des Druckers, Vorgabe `CP1252`; `null` laesst die des Geraets stehen. */
58
+ /**
59
+ * Codepage des Druckers, Vorgabe `CP1252`; `null` laesst die des Geraets
60
+ * stehen. Eine Tabelle des Katalogs (`CodeTableId`, im Drucker-Wizard
61
+ * gewaehlt) druckt `€`, `§`, `°` als echtes Byte, wo die Tabelle sie hat,
62
+ * sonst als Ersatzbuchstaben; ohne Wahl bleibt der Bon byte-gleich.
63
+ */
59
64
  codeTable?: PosCodeTable | null;
60
65
  /** Papierschnitt am Ende: `true` (voll), `'partial'` oder `false`. Vorgabe `true`. */
61
66
  cut?: boolean | PosCutMode;
@@ -34,17 +34,21 @@ export function blattFuerDruck(layout, optionen) {
34
34
  /**
35
35
  * Druckbar gemachtes Layout: Texte durch [escPosPrintableText] (Codepage,
36
36
  * "EUR" statt "€", Striche), damit das Raster mit den Zeichen rechnet, die
37
- * wirklich aufs Papier gehen. Der QR-Inhalt bleibt unveraendert.
37
+ * wirklich aufs Papier gehen. Mit gewaehlter Tabelle bleibt "€", wo die
38
+ * Tabelle es hat, und fehlende Zeichen werden schon hier zu
39
+ * Ersatzbuchstaben: jedes Zeichen ist danach genau ein Byte. Der QR-Inhalt
40
+ * bleibt unveraendert.
38
41
  */
39
- function druckbaresLayout(layout) {
42
+ function druckbaresLayout(layout, codeTable) {
43
+ const druckbar = (text) => escPosPrintableText(text, codeTable);
40
44
  return {
41
45
  ...layout,
42
46
  lines: layout.lines.map((z) => {
43
47
  switch (z.kind) {
44
- case 'text': return { ...z, text: escPosPrintableText(z.text) };
45
- case 'banner': return { ...z, text: escPosPrintableText(z.text) };
46
- case 'columns': return { ...z, columns: z.columns.map((c) => ({ ...c, text: escPosPrintableText(c.text) })) };
47
- case 'rule': return { ...z, char: escPosPrintableText(z.char) || '-' };
48
+ case 'text': return { ...z, text: druckbar(z.text) };
49
+ case 'banner': return { ...z, text: druckbar(z.text) };
50
+ case 'columns': return { ...z, columns: z.columns.map((c) => ({ ...c, text: druckbar(c.text) })) };
51
+ case 'rule': return { ...z, char: druckbar(z.char) || '-' };
48
52
  default: return z;
49
53
  }
50
54
  }),
@@ -134,7 +138,7 @@ export function escPosLayoutResult(layout, options = {}) {
134
138
  };
135
139
  // `blattFuerDruck` prueft das Logo-Raster bereits VOR der Rueckgabe -- die
136
140
  // Schleife unten schreibt darum nie ein Byte auf ein falsch grosses Logo.
137
- const blatt = blattFuerDruck(druckbaresLayout(layout), {
141
+ const blatt = blattFuerDruck(druckbaresLayout(layout, options.codeTable), {
138
142
  zeichen: CHARS_PER_PAPER_SIZE[paperSize],
139
143
  logo: options.logo,
140
144
  marke: options.brandMark,
@@ -4,4 +4,4 @@
4
4
  * Client-Version, Nachtrag §6). Ein Test haelt sie gleich mit `package.json`;
5
5
  * beim Anheben der Version beide Stellen aendern.
6
6
  */
7
- export declare const PACKAGE_VERSION = "1.0.0";
7
+ export declare const PACKAGE_VERSION = "1.1.1";
@@ -4,4 +4,4 @@
4
4
  * Client-Version, Nachtrag §6). Ein Test haelt sie gleich mit `package.json`;
5
5
  * beim Anheben der Version beide Stellen aendern.
6
6
  */
7
- export const PACKAGE_VERSION = '1.0.0';
7
+ export const PACKAGE_VERSION = '1.1.1';
@@ -0,0 +1,14 @@
1
+ {
2
+ "sample": [
3
+ "Käsekrainer 3,50 €",
4
+ "Tee 80°"
5
+ ],
6
+ "tables": {
7
+ "wpc1252": "Käsekrainer 3,50 €\nTee 80°",
8
+ "pc858": "Käsekrainer 3,50 €\nTee 80°",
9
+ "pc850": "Käsekrainer 3,50 EUR\nTee 80°",
10
+ "pc437": "Käsekrainer 3,50 EUR\nTee 80°",
11
+ "iso8859_15": "Käsekrainer 3,50 €\nTee 80°",
12
+ "replacement": "Kaesekrainer 3,50 EUR\nTee 80Grad"
13
+ }
14
+ }
@@ -0,0 +1,84 @@
1
+ {
2
+ "version": 1,
3
+ "_note": "Receipts per code table: ESC/POS bytes of each case for every table in code-tables.json, written by `npm run fixtures:zeichensatz` to expected/code-table-receipt.<case>.<table>.hex (32 bytes per line). A case names a golden receipt under receipts/ or carries its own input. Print options: escPosLayoutBytes(layout, { codeTable }), everything else default.",
4
+ "tables": [
5
+ "wpc1252",
6
+ "pc858",
7
+ "pc850",
8
+ "pc437",
9
+ "iso8859_15",
10
+ "replacement"
11
+ ],
12
+ "cases": [
13
+ {
14
+ "name": "sale-cash",
15
+ "receipt": "sale-cash"
16
+ },
17
+ {
18
+ "name": "test-cashregister-sale",
19
+ "receipt": "test-cashregister-sale"
20
+ },
21
+ {
22
+ "name": "long-item-name",
23
+ "receipt": "long-item-name"
24
+ },
25
+ {
26
+ "name": "special-characters",
27
+ "input": {
28
+ "company": {
29
+ "companyName": "Bäckerei Muster",
30
+ "street": "Hauptplatz 1",
31
+ "zip": "4830",
32
+ "city": "Hallstatt",
33
+ "phone": "+43 6134 12345",
34
+ "vatId": "ATU12345678",
35
+ "taxNumber": "",
36
+ "isSmallBusiness": false,
37
+ "footer1": "Preise in € inkl. USt., Pfand nach § 3",
38
+ "footer2": "Danke & auf Wiedersehen",
39
+ "thanksMessage": [],
40
+ "showKreiseckLogo": false
41
+ },
42
+ "receipt": {
43
+ "receiptId": "AT0-KASSE1-43",
44
+ "cashregisterId": "KASSE1",
45
+ "timeStamp": "2026-08-13T10:15:30",
46
+ "items": [
47
+ {
48
+ "name": "Tee 80° heiß",
49
+ "quantity": 2,
50
+ "unitPriceCents": 320,
51
+ "vatRate": 10
52
+ },
53
+ {
54
+ "name": "Pfand § 3 Mehrweg",
55
+ "quantity": 1,
56
+ "unitPriceCents": 100,
57
+ "vatRate": 20
58
+ },
59
+ {
60
+ "name": "Grüner Öl-Kürbis Größe XL",
61
+ "quantity": 1,
62
+ "unitPriceCents": 1250,
63
+ "vatRate": 10
64
+ }
65
+ ],
66
+ "vouchers": [],
67
+ "paymentMethod": "cash",
68
+ "turnoverCounterAES256ICM": "UMSATZ",
69
+ "signaturePreviousReceipt": "VORGAENGER",
70
+ "certificateSerialNumber": "6F0404F0",
71
+ "receiptType": "standard",
72
+ "sig": "eyJhbGciOiJFUzI1NiJ9.QVQx.SIGNATURWERT",
73
+ "qr": "_R1-AT1_KASSE1_AT0-KASSE1-43_2026-08-13T10:15:30_5,00_2,70_0,00_0,00_0,00_UMSATZ_VORGAENGER_6F0404F0_SIGNATUR",
74
+ "fullReceiptId": "VOLL",
75
+ "customerDetails": [],
76
+ "legalMessage": []
77
+ },
78
+ "options": {
79
+ "paperSize": "mm58"
80
+ }
81
+ }
82
+ }
83
+ ]
84
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 1,
3
+ "characters": ["ä","ö","ü","Ä","Ö","Ü","ß","€","§","°"],
4
+ "tables": [
5
+ {"id":"wpc1252","number":1,"escT":16,"bytes":{"ä":"E4","ö":"F6","ü":"FC","Ä":"C4","Ö":"D6","Ü":"DC","ß":"DF","€":"80","§":"A7","°":"B0"}},
6
+ {"id":"pc858","number":2,"escT":19,"bytes":{"ä":"84","ö":"94","ü":"81","Ä":"8E","Ö":"99","Ü":"9A","ß":"E1","€":"D5","§":"F5","°":"F8"}},
7
+ {"id":"pc850","number":3,"escT":2,"bytes":{"ä":"84","ö":"94","ü":"81","Ä":"8E","Ö":"99","Ü":"9A","ß":"E1","€":"455552","§":"F5","°":"F8"}},
8
+ {"id":"pc437","number":4,"escT":0,"bytes":{"ä":"84","ö":"94","ü":"81","Ä":"8E","Ö":"99","Ü":"9A","ß":"E1","€":"455552","§":"5061722E","°":"F8"}},
9
+ {"id":"iso8859_15","number":5,"escT":40,"bytes":{"ä":"E4","ö":"F6","ü":"FC","Ä":"C4","Ö":"D6","Ü":"DC","ß":"DF","€":"A4","§":"A7","°":"B0"}},
10
+ {"id":"replacement","number":6,"escT":0,"bytes":{"ä":"6165","ö":"6F65","ü":"7565","Ä":"4165","Ö":"4F65","Ü":"5565","ß":"7373","€":"455552","§":"5061722E","°":"47726164"}}
11
+ ]
12
+ }
@@ -0,0 +1,27 @@
1
+ 1b401d4c00001d5780011b74281b45011c2e1b7428202020202020202042e463
2
+ 6b65726569204d75737465720a1b45001c2e1b74282020202020202020202048
3
+ 61757074706c61747a20310a1c2e1b7428202020202020202020343833302048
4
+ 616c6c73746174740a1c2e1b7428202020202020202020204154553132333435
5
+ 3637380a1c2e1b74282020202020202020202b34332036313334203132333435
6
+ 0a1b64011c2e1b7428446174756d3a2020202020202031332e30382e32303236
7
+ 2031303a31353a33300a1c2e1b74284b617373656e2d49443a20202020202020
8
+ 2020202020202020204b41535345310a1c2e1b742842656c65672d49443a2020
9
+ 20202020202020204154302d4b41535345312d34320a1b64011c2e1b74283220
10
+ 2078204861757367656d6163687465202020202020202020322c393820420a1c
11
+ 2e1b742842696f2d44696e6b656c766f6c6c6b6f726e73656d6d656c206d6974
12
+ 0a1c2e1b74284bfc726269736b65726e656e20756e640a1c2e1b7428536f6e6e
13
+ 656e626c756d656e6b65726e656e206a6520312c34390a1b64011c2e1b74284d
14
+ 775374252020204d775374202020204e6574746f202020202042727574746f0a
15
+ 1c2e1b74284220313025202020302c323720202020322c373120202020202020
16
+ 20322c39380a1c2e1b74282d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
17
+ 2d2d2d2d2d2d2d2d2d2d2d0a1c2e1b7428476573616d743a2020202020202020
18
+ 2020202020202020202020322c393820a40a1c2e1b74285a61686c756e677361
19
+ 72743a202020202020202020204261727a61686c756e670a1b64011b61311c2e
20
+ 1b74281d286b03003143061d286b03003145311d286b70003150305f52312d41
21
+ 54315f4b41535345315f4154302d4b41535345312d34325f323032362d30382d
22
+ 31335431303a31353a33305f352c30305f322c37305f302c30305f302c30305f
23
+ 302c30305f554d5341545a5f564f524741454e4745525f36463034303446305f
24
+ 5349474e415455521d286b03003152301d286b03003151301b64011b61301c2e
25
+ 1b7428205669656c656e2044616e6b2066fc7220496872656e2045696e6b6175
26
+ 660a1c2e1b7428202020207777772e626165636b657265692d6d75737465722e
27
+ 61740a0a0a0a0a0a1d5630
@@ -0,0 +1,27 @@
1
+ 1b401d4c00001d5780011b74001b45011c2e1b74002020202020202020428463
2
+ 6b65726569204d75737465720a1b45001c2e1b74002020202020202020202048
3
+ 61757074706c61747a20310a1c2e1b7400202020202020202020343833302048
4
+ 616c6c73746174740a1c2e1b7400202020202020202020204154553132333435
5
+ 3637380a1c2e1b74002020202020202020202b34332036313334203132333435
6
+ 0a1b64011c2e1b7400446174756d3a2020202020202031332e30382e32303236
7
+ 2031303a31353a33300a1c2e1b74004b617373656e2d49443a20202020202020
8
+ 2020202020202020204b41535345310a1c2e1b740042656c65672d49443a2020
9
+ 20202020202020204154302d4b41535345312d34320a1b64011c2e1b74003220
10
+ 2078204861757367656d6163687465202020202020202020322c393820420a1c
11
+ 2e1b740042696f2d44696e6b656c766f6c6c6b6f726e73656d6d656c206d6974
12
+ 0a1c2e1b74004b81726269736b65726e656e20756e640a1c2e1b7400536f6e6e
13
+ 656e626c756d656e6b65726e656e206a6520312c34390a1b64011c2e1b74004d
14
+ 775374252020204d775374202020204e6574746f202020202042727574746f0a
15
+ 1c2e1b74004220313025202020302c323720202020322c373120202020202020
16
+ 20322c39380a1c2e1b74002d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
17
+ 2d2d2d2d2d2d2d2d2d2d2d0a1c2e1b7400476573616d743a2020202020202020
18
+ 202020202020202020322c3938204555520a1c2e1b74005a61686c756e677361
19
+ 72743a202020202020202020204261727a61686c756e670a1b64011b61311c2e
20
+ 1b74001d286b03003143061d286b03003145311d286b70003150305f52312d41
21
+ 54315f4b41535345315f4154302d4b41535345312d34325f323032362d30382d
22
+ 31335431303a31353a33305f352c30305f322c37305f302c30305f302c30305f
23
+ 302c30305f554d5341545a5f564f524741454e4745525f36463034303446305f
24
+ 5349474e415455521d286b03003152301d286b03003151301b64011b61301c2e
25
+ 1b7400205669656c656e2044616e6b2066817220496872656e2045696e6b6175
26
+ 660a1c2e1b7400202020207777772e626165636b657265692d6d75737465722e
27
+ 61740a0a0a0a0a0a1d5630
@@ -0,0 +1,27 @@
1
+ 1b401d4c00001d5780011b74021b45011c2e1b74022020202020202020428463
2
+ 6b65726569204d75737465720a1b45001c2e1b74022020202020202020202048
3
+ 61757074706c61747a20310a1c2e1b7402202020202020202020343833302048
4
+ 616c6c73746174740a1c2e1b7402202020202020202020204154553132333435
5
+ 3637380a1c2e1b74022020202020202020202b34332036313334203132333435
6
+ 0a1b64011c2e1b7402446174756d3a2020202020202031332e30382e32303236
7
+ 2031303a31353a33300a1c2e1b74024b617373656e2d49443a20202020202020
8
+ 2020202020202020204b41535345310a1c2e1b740242656c65672d49443a2020
9
+ 20202020202020204154302d4b41535345312d34320a1b64011c2e1b74023220
10
+ 2078204861757367656d6163687465202020202020202020322c393820420a1c
11
+ 2e1b740242696f2d44696e6b656c766f6c6c6b6f726e73656d6d656c206d6974
12
+ 0a1c2e1b74024b81726269736b65726e656e20756e640a1c2e1b7402536f6e6e
13
+ 656e626c756d656e6b65726e656e206a6520312c34390a1b64011c2e1b74024d
14
+ 775374252020204d775374202020204e6574746f202020202042727574746f0a
15
+ 1c2e1b74024220313025202020302c323720202020322c373120202020202020
16
+ 20322c39380a1c2e1b74022d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
17
+ 2d2d2d2d2d2d2d2d2d2d2d0a1c2e1b7402476573616d743a2020202020202020
18
+ 202020202020202020322c3938204555520a1c2e1b74025a61686c756e677361
19
+ 72743a202020202020202020204261727a61686c756e670a1b64011b61311c2e
20
+ 1b74021d286b03003143061d286b03003145311d286b70003150305f52312d41
21
+ 54315f4b41535345315f4154302d4b41535345312d34325f323032362d30382d
22
+ 31335431303a31353a33305f352c30305f322c37305f302c30305f302c30305f
23
+ 302c30305f554d5341545a5f564f524741454e4745525f36463034303446305f
24
+ 5349474e415455521d286b03003152301d286b03003151301b64011b61301c2e
25
+ 1b7402205669656c656e2044616e6b2066817220496872656e2045696e6b6175
26
+ 660a1c2e1b7402202020207777772e626165636b657265692d6d75737465722e
27
+ 61740a0a0a0a0a0a1d5630
@@ -0,0 +1,27 @@
1
+ 1b401d4c00001d5780011b74131b45011c2e1b74132020202020202020428463
2
+ 6b65726569204d75737465720a1b45001c2e1b74132020202020202020202048
3
+ 61757074706c61747a20310a1c2e1b7413202020202020202020343833302048
4
+ 616c6c73746174740a1c2e1b7413202020202020202020204154553132333435
5
+ 3637380a1c2e1b74132020202020202020202b34332036313334203132333435
6
+ 0a1b64011c2e1b7413446174756d3a2020202020202031332e30382e32303236
7
+ 2031303a31353a33300a1c2e1b74134b617373656e2d49443a20202020202020
8
+ 2020202020202020204b41535345310a1c2e1b741342656c65672d49443a2020
9
+ 20202020202020204154302d4b41535345312d34320a1b64011c2e1b74133220
10
+ 2078204861757367656d6163687465202020202020202020322c393820420a1c
11
+ 2e1b741342696f2d44696e6b656c766f6c6c6b6f726e73656d6d656c206d6974
12
+ 0a1c2e1b74134b81726269736b65726e656e20756e640a1c2e1b7413536f6e6e
13
+ 656e626c756d656e6b65726e656e206a6520312c34390a1b64011c2e1b74134d
14
+ 775374252020204d775374202020204e6574746f202020202042727574746f0a
15
+ 1c2e1b74134220313025202020302c323720202020322c373120202020202020
16
+ 20322c39380a1c2e1b74132d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
17
+ 2d2d2d2d2d2d2d2d2d2d2d0a1c2e1b7413476573616d743a2020202020202020
18
+ 2020202020202020202020322c393820d50a1c2e1b74135a61686c756e677361
19
+ 72743a202020202020202020204261727a61686c756e670a1b64011b61311c2e
20
+ 1b74131d286b03003143061d286b03003145311d286b70003150305f52312d41
21
+ 54315f4b41535345315f4154302d4b41535345312d34325f323032362d30382d
22
+ 31335431303a31353a33305f352c30305f322c37305f302c30305f302c30305f
23
+ 302c30305f554d5341545a5f564f524741454e4745525f36463034303446305f
24
+ 5349474e415455521d286b03003152301d286b03003151301b64011b61301c2e
25
+ 1b7413205669656c656e2044616e6b2066817220496872656e2045696e6b6175
26
+ 660a1c2e1b7413202020207777772e626165636b657265692d6d75737465722e
27
+ 61740a0a0a0a0a0a1d5630