estela 0.1.38 → 0.1.40
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/README.md +70 -0
- package/dist/cli-badge.test.d.ts +2 -0
- package/dist/cli-badge.test.d.ts.map +1 -0
- package/dist/cli-badge.test.js +118 -0
- package/dist/cli-badge.test.js.map +1 -0
- package/dist/cli-quota.test.d.ts +2 -0
- package/dist/cli-quota.test.d.ts.map +1 -0
- package/dist/cli-quota.test.js +216 -0
- package/dist/cli-quota.test.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +268 -0
- package/dist/cli.js.map +1 -1
- package/dist/db/store.d.ts +10 -1
- package/dist/db/store.d.ts.map +1 -1
- package/dist/db/store.js +29 -0
- package/dist/db/store.js.map +1 -1
- package/dist/demo.d.ts.map +1 -1
- package/dist/demo.js +53 -0
- package/dist/demo.js.map +1 -1
- package/dist/export/badge.d.ts +26 -0
- package/dist/export/badge.d.ts.map +1 -0
- package/dist/export/badge.js +37 -0
- package/dist/export/badge.js.map +1 -0
- package/dist/export/badge.test.d.ts +2 -0
- package/dist/export/badge.test.d.ts.map +1 -0
- package/dist/export/badge.test.js +32 -0
- package/dist/export/badge.test.js.map +1 -0
- package/dist/export/idioma.test.js +6 -2
- package/dist/export/idioma.test.js.map +1 -1
- package/dist/export/panel.d.ts +7 -0
- package/dist/export/panel.d.ts.map +1 -1
- package/dist/export/panel.js +25 -6
- package/dist/export/panel.js.map +1 -1
- package/dist/export/panel.test.js +17 -4
- package/dist/export/panel.test.js.map +1 -1
- package/dist/export/pixelfont.d.ts +17 -0
- package/dist/export/pixelfont.d.ts.map +1 -0
- package/dist/export/pixelfont.js +157 -0
- package/dist/export/pixelfont.js.map +1 -0
- package/dist/export/png.d.ts +12 -0
- package/dist/export/png.d.ts.map +1 -0
- package/dist/export/png.js +85 -0
- package/dist/export/png.js.map +1 -0
- package/dist/export/receipt.d.ts +58 -0
- package/dist/export/receipt.d.ts.map +1 -0
- package/dist/export/receipt.js +216 -0
- package/dist/export/receipt.js.map +1 -0
- package/dist/export/receipt.test.d.ts +2 -0
- package/dist/export/receipt.test.d.ts.map +1 -0
- package/dist/export/receipt.test.js +207 -0
- package/dist/export/receipt.test.js.map +1 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +54 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/metrics/quota.d.ts +186 -0
- package/dist/metrics/quota.d.ts.map +1 -0
- package/dist/metrics/quota.js +245 -0
- package/dist/metrics/quota.js.map +1 -0
- package/dist/metrics/quota.test.d.ts +2 -0
- package/dist/metrics/quota.test.d.ts.map +1 -0
- package/dist/metrics/quota.test.js +262 -0
- package/dist/metrics/quota.test.js.map +1 -0
- package/dist/publish.d.ts +2 -0
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +3 -1
- package/dist/publish.js.map +1 -1
- package/dist/quota.d.ts +18 -0
- package/dist/quota.d.ts.map +1 -0
- package/dist/quota.js +85 -0
- package/dist/quota.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -0
- package/dist/server.js.map +1 -1
- package/dist/server.test.js +37 -0
- package/dist/server.test.js.map +1 -1
- package/node_modules/@estela/shared/dist/money.d.ts +6 -0
- package/node_modules/@estela/shared/dist/money.d.ts.map +1 -1
- package/node_modules/@estela/shared/dist/money.js +11 -0
- package/node_modules/@estela/shared/dist/money.js.map +1 -1
- package/package.json +1 -1
- package/web/app.css +36 -0
- package/web/app.js +61 -1
- package/web/i18n.js +28 -0
- package/web/index.html +4 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Lienzo } from "./png.js";
|
|
2
|
+
export declare const GLYPH_W = 5;
|
|
3
|
+
export declare const GLYPH_H = 9;
|
|
4
|
+
/** Ancho de una letra más su separación, en píxeles de la fuente. */
|
|
5
|
+
export declare const ADVANCE: number;
|
|
6
|
+
export declare function hasGlyph(ch: string): boolean;
|
|
7
|
+
type Color = readonly [number, number, number];
|
|
8
|
+
/**
|
|
9
|
+
* Pinta `texto` con su esquina superior izquierda en (x, y). `escala` es
|
|
10
|
+
* cuántos píxeles de la imagen mide cada píxel de la fuente. `negrita` lo
|
|
11
|
+
* pinta dos veces, desplazado uno: es como la hace una impresora de tickets.
|
|
12
|
+
*/
|
|
13
|
+
export declare function drawText(lienzo: Lienzo, texto: string, x: number, y: number, escala: number, color: Color, negrita?: boolean): void;
|
|
14
|
+
/** Ancho en píxeles de la imagen de un texto a esa escala. */
|
|
15
|
+
export declare function textWidth(texto: string, escala: number): number;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=pixelfont.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixelfont.d.ts","sourceRoot":"","sources":["../../src/export/pixelfont.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AA8HvC,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,qEAAqE;AACrE,eAAO,MAAM,OAAO,QAAc,CAAC;AAWnC,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE5C;AAED,KAAK,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,UAAQ,GACjG,IAAI,CAaN;AAED,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/D"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ADVANCE = exports.GLYPH_H = exports.GLYPH_W = void 0;
|
|
4
|
+
exports.hasGlyph = hasGlyph;
|
|
5
|
+
exports.drawText = drawText;
|
|
6
|
+
exports.textWidth = textWidth;
|
|
7
|
+
const G = {
|
|
8
|
+
" ": ["00000", "00000", "00000", "00000", "00000", "00000", "00000"],
|
|
9
|
+
A: ["01110", "10001", "10001", "11111", "10001", "10001", "10001"],
|
|
10
|
+
B: ["11110", "10001", "10001", "11110", "10001", "10001", "11110"],
|
|
11
|
+
C: ["01110", "10001", "10000", "10000", "10000", "10001", "01110"],
|
|
12
|
+
D: ["11100", "10010", "10001", "10001", "10001", "10010", "11100"],
|
|
13
|
+
E: ["11111", "10000", "10000", "11110", "10000", "10000", "11111"],
|
|
14
|
+
F: ["11111", "10000", "10000", "11110", "10000", "10000", "10000"],
|
|
15
|
+
G: ["01110", "10001", "10000", "10111", "10001", "10001", "01111"],
|
|
16
|
+
H: ["10001", "10001", "10001", "11111", "10001", "10001", "10001"],
|
|
17
|
+
I: ["01110", "00100", "00100", "00100", "00100", "00100", "01110"],
|
|
18
|
+
J: ["00111", "00010", "00010", "00010", "00010", "10010", "01100"],
|
|
19
|
+
K: ["10001", "10010", "10100", "11000", "10100", "10010", "10001"],
|
|
20
|
+
L: ["10000", "10000", "10000", "10000", "10000", "10000", "11111"],
|
|
21
|
+
M: ["10001", "11011", "10101", "10101", "10001", "10001", "10001"],
|
|
22
|
+
N: ["10001", "10001", "11001", "10101", "10011", "10001", "10001"],
|
|
23
|
+
O: ["01110", "10001", "10001", "10001", "10001", "10001", "01110"],
|
|
24
|
+
P: ["11110", "10001", "10001", "11110", "10000", "10000", "10000"],
|
|
25
|
+
Q: ["01110", "10001", "10001", "10001", "10101", "10010", "01101"],
|
|
26
|
+
R: ["11110", "10001", "10001", "11110", "10100", "10010", "10001"],
|
|
27
|
+
S: ["01111", "10000", "10000", "01110", "00001", "00001", "11110"],
|
|
28
|
+
T: ["11111", "00100", "00100", "00100", "00100", "00100", "00100"],
|
|
29
|
+
U: ["10001", "10001", "10001", "10001", "10001", "10001", "01110"],
|
|
30
|
+
V: ["10001", "10001", "10001", "10001", "10001", "01010", "00100"],
|
|
31
|
+
W: ["10001", "10001", "10001", "10101", "10101", "10101", "01010"],
|
|
32
|
+
X: ["10001", "10001", "01010", "00100", "01010", "10001", "10001"],
|
|
33
|
+
Y: ["10001", "10001", "10001", "01010", "00100", "00100", "00100"],
|
|
34
|
+
Z: ["11111", "00001", "00010", "00100", "01000", "10000", "11111"],
|
|
35
|
+
a: ["00000", "00000", "01110", "00001", "01111", "10001", "01111"],
|
|
36
|
+
b: ["10000", "10000", "10110", "11001", "10001", "10001", "11110"],
|
|
37
|
+
c: ["00000", "00000", "01110", "10000", "10000", "10001", "01110"],
|
|
38
|
+
d: ["00001", "00001", "01101", "10011", "10001", "10001", "01111"],
|
|
39
|
+
e: ["00000", "00000", "01110", "10001", "11111", "10000", "01110"],
|
|
40
|
+
f: ["00110", "01001", "01000", "11100", "01000", "01000", "01000"],
|
|
41
|
+
g: ["00000", "01111", "10001", "10001", "01111", "00001", "01110"],
|
|
42
|
+
h: ["10000", "10000", "10110", "11001", "10001", "10001", "10001"],
|
|
43
|
+
i: ["00100", "00000", "01100", "00100", "00100", "00100", "01110"],
|
|
44
|
+
j: ["00010", "00000", "00110", "00010", "00010", "10010", "01100"],
|
|
45
|
+
k: ["10000", "10000", "10010", "10100", "11000", "10100", "10010"],
|
|
46
|
+
l: ["01100", "00100", "00100", "00100", "00100", "00100", "01110"],
|
|
47
|
+
m: ["00000", "00000", "11010", "10101", "10101", "10001", "10001"],
|
|
48
|
+
n: ["00000", "00000", "10110", "11001", "10001", "10001", "10001"],
|
|
49
|
+
o: ["00000", "00000", "01110", "10001", "10001", "10001", "01110"],
|
|
50
|
+
p: ["00000", "00000", "11110", "10001", "11110", "10000", "10000"],
|
|
51
|
+
q: ["00000", "00000", "01101", "10011", "01111", "00001", "00001"],
|
|
52
|
+
r: ["00000", "00000", "10110", "11001", "10000", "10000", "10000"],
|
|
53
|
+
s: ["00000", "00000", "01110", "10000", "01110", "00001", "11110"],
|
|
54
|
+
t: ["01000", "01000", "11100", "01000", "01000", "01001", "00110"],
|
|
55
|
+
u: ["00000", "00000", "10001", "10001", "10001", "10011", "01101"],
|
|
56
|
+
v: ["00000", "00000", "10001", "10001", "10001", "01010", "00100"],
|
|
57
|
+
w: ["00000", "00000", "10001", "10001", "10101", "10101", "01010"],
|
|
58
|
+
x: ["00000", "00000", "10001", "01010", "00100", "01010", "10001"],
|
|
59
|
+
y: ["00000", "00000", "10001", "10001", "01111", "00001", "01110"],
|
|
60
|
+
z: ["00000", "00000", "11111", "00010", "00100", "01000", "11111"],
|
|
61
|
+
"0": ["01110", "10001", "10011", "10101", "11001", "10001", "01110"],
|
|
62
|
+
"1": ["00100", "01100", "00100", "00100", "00100", "00100", "01110"],
|
|
63
|
+
"2": ["01110", "10001", "00001", "00010", "00100", "01000", "11111"],
|
|
64
|
+
"3": ["11111", "00010", "00100", "00010", "00001", "10001", "01110"],
|
|
65
|
+
"4": ["00010", "00110", "01010", "10010", "11111", "00010", "00010"],
|
|
66
|
+
"5": ["11111", "10000", "11110", "00001", "00001", "10001", "01110"],
|
|
67
|
+
"6": ["00110", "01000", "10000", "11110", "10001", "10001", "01110"],
|
|
68
|
+
"7": ["11111", "00001", "00010", "00100", "01000", "01000", "01000"],
|
|
69
|
+
"8": ["01110", "10001", "10001", "01110", "10001", "10001", "01110"],
|
|
70
|
+
"9": ["01110", "10001", "10001", "01111", "00001", "00010", "01100"],
|
|
71
|
+
".": ["00000", "00000", "00000", "00000", "00000", "01100", "01100"],
|
|
72
|
+
",": ["00000", "00000", "00000", "00000", "01100", "00100", "01000"],
|
|
73
|
+
":": ["00000", "01100", "01100", "00000", "01100", "01100", "00000"],
|
|
74
|
+
";": ["00000", "01100", "01100", "00000", "01100", "00100", "01000"],
|
|
75
|
+
"-": ["00000", "00000", "00000", "11111", "00000", "00000", "00000"],
|
|
76
|
+
"·": ["00000", "00000", "00000", "00100", "00000", "00000", "00000"],
|
|
77
|
+
"/": ["00000", "00001", "00010", "00100", "01000", "10000", "00000"],
|
|
78
|
+
"$": ["00100", "01111", "10100", "01110", "00101", "11110", "00100"],
|
|
79
|
+
"€": ["00111", "01000", "11110", "01000", "11110", "01000", "00111"],
|
|
80
|
+
"£": ["00110", "01001", "01000", "11100", "01000", "01000", "11111"],
|
|
81
|
+
"%": ["11000", "11001", "00010", "00100", "01000", "10011", "00011"],
|
|
82
|
+
"(": ["00010", "00100", "01000", "01000", "01000", "00100", "00010"],
|
|
83
|
+
")": ["01000", "00100", "00010", "00010", "00010", "00100", "01000"],
|
|
84
|
+
"'": ["01100", "00100", "01000", "00000", "00000", "00000", "00000"],
|
|
85
|
+
"\"": ["01010", "01010", "01010", "00000", "00000", "00000", "00000"],
|
|
86
|
+
"!": ["00100", "00100", "00100", "00100", "00100", "00000", "00100"],
|
|
87
|
+
"?": ["01110", "10001", "00001", "00010", "00100", "00000", "00100"],
|
|
88
|
+
"¿": ["00100", "00000", "00100", "01000", "10000", "10001", "01110"],
|
|
89
|
+
"¡": ["00100", "00000", "00100", "00100", "00100", "00100", "00100"],
|
|
90
|
+
"<": ["00010", "00100", "01000", "10000", "01000", "00100", "00010"],
|
|
91
|
+
">": ["01000", "00100", "00010", "00001", "00010", "00100", "01000"],
|
|
92
|
+
"+": ["00000", "00100", "00100", "11111", "00100", "00100", "00000"],
|
|
93
|
+
"=": ["00000", "00000", "11111", "00000", "11111", "00000", "00000"],
|
|
94
|
+
"_": ["00000", "00000", "00000", "00000", "00000", "00000", "11111"],
|
|
95
|
+
"#": ["01010", "01010", "11111", "01010", "11111", "01010", "01010"],
|
|
96
|
+
"&": ["01100", "10010", "10100", "01000", "10101", "10010", "01101"],
|
|
97
|
+
"*": ["00000", "00100", "10101", "01110", "10101", "00100", "00000"],
|
|
98
|
+
"@": ["01110", "10001", "00001", "01101", "10101", "10101", "01110"],
|
|
99
|
+
"[": ["01110", "01000", "01000", "01000", "01000", "01000", "01110"],
|
|
100
|
+
"]": ["01110", "00010", "00010", "00010", "00010", "00010", "01110"],
|
|
101
|
+
"|": ["00100", "00100", "00100", "00100", "00100", "00100", "00100"],
|
|
102
|
+
"~": ["00000", "00000", "01000", "10101", "00010", "00000", "00000"],
|
|
103
|
+
};
|
|
104
|
+
const AGUDO = ["00010", "00100"];
|
|
105
|
+
const DIERESIS = ["01010", "00000"];
|
|
106
|
+
const VIRGULILLA = ["01101", "10110"];
|
|
107
|
+
const SIN_ACENTO = ["00000", "00000"];
|
|
108
|
+
/** Letra con acento → [acento, letra base]. La "í" pierde el punto: lo sustituye la tilde. */
|
|
109
|
+
const ACENTUADAS = {
|
|
110
|
+
"Á": [AGUDO, G["A"]], "É": [AGUDO, G["E"]], "Í": [AGUDO, G["I"]], "Ó": [AGUDO, G["O"]],
|
|
111
|
+
"Ú": [AGUDO, G["U"]], "Ñ": [VIRGULILLA, G["N"]], "Ü": [DIERESIS, G["U"]],
|
|
112
|
+
"á": [AGUDO, G["a"]], "é": [AGUDO, G["e"]], "ó": [AGUDO, G["o"]], "ú": [AGUDO, G["u"]],
|
|
113
|
+
"í": [AGUDO, ["00000", "00000", "01100", "00100", "00100", "00100", "01110"]],
|
|
114
|
+
"ñ": [VIRGULILLA, G["n"]], "ü": [DIERESIS, G["u"]],
|
|
115
|
+
};
|
|
116
|
+
exports.GLYPH_W = 5;
|
|
117
|
+
exports.GLYPH_H = 9;
|
|
118
|
+
/** Ancho de una letra más su separación, en píxeles de la fuente. */
|
|
119
|
+
exports.ADVANCE = exports.GLYPH_W + 1;
|
|
120
|
+
/** Las 9 filas de un carácter: 2 de acento y 7 de letra. */
|
|
121
|
+
function glifo(ch) {
|
|
122
|
+
const acentuada = ACENTUADAS[ch];
|
|
123
|
+
if (acentuada)
|
|
124
|
+
return [...acentuada[0], ...acentuada[1]];
|
|
125
|
+
// Las comillas y guiones tipográficos se pintan con su equivalente simple.
|
|
126
|
+
const simple = { "–": "-", "—": "-", "’": "'", "‘": "'", "“": "\"", "”": "\"" }[ch];
|
|
127
|
+
return [...SIN_ACENTO, ...(G[simple ?? ch] ?? G["?"])];
|
|
128
|
+
}
|
|
129
|
+
function hasGlyph(ch) {
|
|
130
|
+
return ch in G || ch in ACENTUADAS || "–—’‘“”".includes(ch);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Pinta `texto` con su esquina superior izquierda en (x, y). `escala` es
|
|
134
|
+
* cuántos píxeles de la imagen mide cada píxel de la fuente. `negrita` lo
|
|
135
|
+
* pinta dos veces, desplazado uno: es como la hace una impresora de tickets.
|
|
136
|
+
*/
|
|
137
|
+
function drawText(lienzo, texto, x, y, escala, color, negrita = false) {
|
|
138
|
+
let cx = x;
|
|
139
|
+
for (const ch of texto) {
|
|
140
|
+
const filas = glifo(ch);
|
|
141
|
+
for (let fy = 0; fy < exports.GLYPH_H; fy++) {
|
|
142
|
+
for (let fx = 0; fx < exports.GLYPH_W; fx++) {
|
|
143
|
+
if (filas[fy][fx] !== "1")
|
|
144
|
+
continue;
|
|
145
|
+
lienzo.rect(cx + fx * escala, y + fy * escala, escala, escala, color);
|
|
146
|
+
if (negrita)
|
|
147
|
+
lienzo.rect(cx + fx * escala + Math.max(1, escala >> 1), y + fy * escala, escala, escala, color);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
cx += exports.ADVANCE * escala;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Ancho en píxeles de la imagen de un texto a esa escala. */
|
|
154
|
+
function textWidth(texto, escala) {
|
|
155
|
+
return [...texto].length * exports.ADVANCE * escala;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=pixelfont.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixelfont.js","sourceRoot":"","sources":["../../src/export/pixelfont.ts"],"names":[],"mappings":";;;AA4IA,4BAEC;AASD,4BAeC;AAGD,8BAEC;AA7JD,MAAM,CAAC,GAA0B;IAC/B,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IAClE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACrE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function crc32(buf: Uint8Array): number;
|
|
2
|
+
/** Lienzo RGB: `set` pinta un píxel, `png` lo codifica. */
|
|
3
|
+
export declare class Lienzo {
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly pixels: Uint8Array;
|
|
7
|
+
constructor(width: number, height: number, fondo: readonly [number, number, number]);
|
|
8
|
+
set(x: number, y: number, color: readonly [number, number, number]): void;
|
|
9
|
+
rect(x: number, y: number, w: number, h: number, color: readonly [number, number, number]): void;
|
|
10
|
+
png(): Buffer;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=png.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"png.d.ts","sourceRoot":"","sources":["../../src/export/png.ts"],"names":[],"mappings":"AAuBA,wBAAgB,KAAK,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAI7C;AAWD,2DAA2D;AAC3D,qBAAa,MAAM;IAGL,QAAQ,CAAC,KAAK,EAAE,MAAM;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM;IAF3D,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;gBAEP,KAAK,EAAE,MAAM,EAAW,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;IAKrG,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAKzE,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAIhG,GAAG,IAAI,MAAM;CAwBd"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Lienzo = void 0;
|
|
4
|
+
exports.crc32 = crc32;
|
|
5
|
+
const node_zlib_1 = require("node:zlib");
|
|
6
|
+
/**
|
|
7
|
+
* Un PNG a partir de píxeles RGB, sin dependencias.
|
|
8
|
+
*
|
|
9
|
+
* Estela no tiene dependencias en tiempo de ejecución a propósito (ver el
|
|
10
|
+
* README), y para un recibo de colores planos no hace falta más que esto: la
|
|
11
|
+
* cabecera, los píxeles comprimidos con el zlib de Node y el cierre, cada
|
|
12
|
+
* trozo con su CRC. Nada de paletas ni transparencia.
|
|
13
|
+
*/
|
|
14
|
+
const FIRMA = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
15
|
+
const CRC_TABLA = (() => {
|
|
16
|
+
const t = new Uint32Array(256);
|
|
17
|
+
for (let n = 0; n < 256; n++) {
|
|
18
|
+
let c = n;
|
|
19
|
+
for (let k = 0; k < 8; k++)
|
|
20
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
21
|
+
t[n] = c >>> 0;
|
|
22
|
+
}
|
|
23
|
+
return t;
|
|
24
|
+
})();
|
|
25
|
+
function crc32(buf) {
|
|
26
|
+
let c = 0xffffffff;
|
|
27
|
+
for (const b of buf)
|
|
28
|
+
c = CRC_TABLA[(c ^ b) & 0xff] ^ (c >>> 8);
|
|
29
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
30
|
+
}
|
|
31
|
+
function trozo(tipo, datos) {
|
|
32
|
+
const largo = Buffer.alloc(4);
|
|
33
|
+
largo.writeUInt32BE(datos.length);
|
|
34
|
+
const cuerpo = Buffer.concat([Buffer.from(tipo, "ascii"), datos]);
|
|
35
|
+
const crc = Buffer.alloc(4);
|
|
36
|
+
crc.writeUInt32BE(crc32(cuerpo));
|
|
37
|
+
return Buffer.concat([largo, cuerpo, crc]);
|
|
38
|
+
}
|
|
39
|
+
/** Lienzo RGB: `set` pinta un píxel, `png` lo codifica. */
|
|
40
|
+
class Lienzo {
|
|
41
|
+
width;
|
|
42
|
+
height;
|
|
43
|
+
pixels;
|
|
44
|
+
constructor(width, height, fondo) {
|
|
45
|
+
this.width = width;
|
|
46
|
+
this.height = height;
|
|
47
|
+
this.pixels = new Uint8Array(width * height * 3);
|
|
48
|
+
for (let i = 0; i < width * height; i++)
|
|
49
|
+
this.pixels.set(fondo, i * 3);
|
|
50
|
+
}
|
|
51
|
+
set(x, y, color) {
|
|
52
|
+
if (x < 0 || y < 0 || x >= this.width || y >= this.height)
|
|
53
|
+
return;
|
|
54
|
+
this.pixels.set(color, (y * this.width + x) * 3);
|
|
55
|
+
}
|
|
56
|
+
rect(x, y, w, h, color) {
|
|
57
|
+
for (let j = 0; j < h; j++)
|
|
58
|
+
for (let i = 0; i < w; i++)
|
|
59
|
+
this.set(x + i, y + j, color);
|
|
60
|
+
}
|
|
61
|
+
png() {
|
|
62
|
+
const cabecera = Buffer.alloc(13);
|
|
63
|
+
cabecera.writeUInt32BE(this.width, 0);
|
|
64
|
+
cabecera.writeUInt32BE(this.height, 4);
|
|
65
|
+
cabecera[8] = 8; // bits por canal
|
|
66
|
+
cabecera[9] = 2; // RGB
|
|
67
|
+
// 10, 11, 12: compresión, filtro y entrelazado, todos 0.
|
|
68
|
+
// Cada fila empieza por el tipo de filtro; 0 = sin filtro. Con colores
|
|
69
|
+
// planos, deflate ya comprime las filas repetidas de sobra.
|
|
70
|
+
const fila = this.width * 3;
|
|
71
|
+
const crudo = Buffer.alloc((fila + 1) * this.height);
|
|
72
|
+
for (let y = 0; y < this.height; y++) {
|
|
73
|
+
crudo[y * (fila + 1)] = 0;
|
|
74
|
+
crudo.set(this.pixels.subarray(y * fila, (y + 1) * fila), y * (fila + 1) + 1);
|
|
75
|
+
}
|
|
76
|
+
return Buffer.concat([
|
|
77
|
+
FIRMA,
|
|
78
|
+
trozo("IHDR", cabecera),
|
|
79
|
+
trozo("IDAT", (0, node_zlib_1.deflateSync)(crudo, { level: 9 })),
|
|
80
|
+
trozo("IEND", Buffer.alloc(0)),
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.Lienzo = Lienzo;
|
|
85
|
+
//# sourceMappingURL=png.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"png.js","sourceRoot":"","sources":["../../src/export/png.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { AiCost, TimeEntry } from "@estela/shared";
|
|
2
|
+
/**
|
|
3
|
+
* `estela receipt`: un ticket en PNG con lo que dejó tu trabajo.
|
|
4
|
+
*
|
|
5
|
+
* Todo se calcula y se pinta en esta máquina, y el fichero se queda en disco:
|
|
6
|
+
* compartirlo es decisión de quien lo tiene. Por eso lleva lo que ya enseña el
|
|
7
|
+
* panel local (horas medidas frente a estimadas, lo que costó la IA) y nada
|
|
8
|
+
* que no se pueda ver ahí.
|
|
9
|
+
*/
|
|
10
|
+
/** Lo que hace falta de un turno de agente para saber cuánto ahorró la caché. */
|
|
11
|
+
export interface ReceiptTurn {
|
|
12
|
+
readonly at: Date;
|
|
13
|
+
readonly model: string;
|
|
14
|
+
readonly cacheRead: number;
|
|
15
|
+
readonly cacheWrite5m: number;
|
|
16
|
+
readonly cacheWrite1h: number;
|
|
17
|
+
}
|
|
18
|
+
export interface ReceiptInput {
|
|
19
|
+
readonly entries: readonly TimeEntry[];
|
|
20
|
+
readonly projectNames: ReadonlyMap<string, string>;
|
|
21
|
+
readonly turns: readonly ReceiptTurn[];
|
|
22
|
+
/** Desde cuándo; null = desde el primer bloque. */
|
|
23
|
+
readonly from: Date | null;
|
|
24
|
+
/** Hasta cuándo, sin incluir. */
|
|
25
|
+
readonly to: Date;
|
|
26
|
+
}
|
|
27
|
+
export interface ReceiptData {
|
|
28
|
+
readonly from: Date;
|
|
29
|
+
/** Último día incluido. */
|
|
30
|
+
readonly to: Date;
|
|
31
|
+
readonly measuredSeconds: number;
|
|
32
|
+
readonly estimatedSeconds: number;
|
|
33
|
+
readonly manualSeconds: number;
|
|
34
|
+
/** Consumo de IA a tarifa API. */
|
|
35
|
+
readonly aiCost: AiCost;
|
|
36
|
+
readonly topProject: {
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly cost: AiCost;
|
|
39
|
+
} | null;
|
|
40
|
+
readonly topFeature: {
|
|
41
|
+
readonly name: string;
|
|
42
|
+
readonly project: string;
|
|
43
|
+
readonly cost: AiCost;
|
|
44
|
+
} | null;
|
|
45
|
+
/** Lo que se habría pagado sin caché, menos lo que se pagó con ella. Nunca negativo. */
|
|
46
|
+
readonly cacheSavings: AiCost;
|
|
47
|
+
}
|
|
48
|
+
export declare function receiptData(input: ReceiptInput): ReceiptData;
|
|
49
|
+
/** El texto de cada línea del recibo, tal como se pinta. Para los tests. */
|
|
50
|
+
export declare function receiptText(d: ReceiptData, options: {
|
|
51
|
+
week: boolean;
|
|
52
|
+
names?: boolean;
|
|
53
|
+
}): string[];
|
|
54
|
+
export declare function renderReceipt(d: ReceiptData, options: {
|
|
55
|
+
week: boolean;
|
|
56
|
+
names?: boolean;
|
|
57
|
+
}): Buffer;
|
|
58
|
+
//# sourceMappingURL=receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../../src/export/receipt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQxD;;;;;;;GAOG;AAEH,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7E,QAAQ,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACvG,wFAAwF;IACxF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAiD5D;AAgHD,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,EAAE,CAGjG;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAmEjG"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.receiptData = receiptData;
|
|
4
|
+
exports.receiptText = receiptText;
|
|
5
|
+
exports.renderReceipt = renderReceipt;
|
|
6
|
+
const shared_1 = require("@estela/shared");
|
|
7
|
+
const index_js_1 = require("../i18n/index.js");
|
|
8
|
+
const catalog_js_1 = require("../pricing/catalog.js");
|
|
9
|
+
const pixelfont_js_1 = require("./pixelfont.js");
|
|
10
|
+
const png_js_1 = require("./png.js");
|
|
11
|
+
function receiptData(input) {
|
|
12
|
+
const dentro = input.entries.filter((e) => (!input.from || e.startedAt >= input.from) && e.startedAt < input.to);
|
|
13
|
+
const from = input.from ?? (dentro.length
|
|
14
|
+
? new Date(Math.min(...dentro.map((e) => e.startedAt.getTime()))) : input.to);
|
|
15
|
+
const suma = (fuente) => dentro.filter((e) => e.source === fuente).reduce((n, e) => n + e.seconds, 0);
|
|
16
|
+
const porProyecto = new Map();
|
|
17
|
+
const porRama = new Map();
|
|
18
|
+
for (const e of dentro) {
|
|
19
|
+
porProyecto.set(e.projectId, (porProyecto.get(e.projectId) ?? 0) + e.aiCost.microUsd);
|
|
20
|
+
if (!e.branch)
|
|
21
|
+
continue;
|
|
22
|
+
const clave = `${e.projectId}\u0000${e.branch}`;
|
|
23
|
+
const previo = porRama.get(clave);
|
|
24
|
+
porRama.set(clave, { project: e.projectId, micro: (previo?.micro ?? 0) + e.aiCost.microUsd });
|
|
25
|
+
}
|
|
26
|
+
const nombre = (id) => input.projectNames.get(id) ?? id;
|
|
27
|
+
const [pId, pMicro] = [...porProyecto].sort((a, b) => b[1] - a[1])[0] ?? [null, 0];
|
|
28
|
+
const [rClave, r] = [...porRama].sort((a, b) => b[1].micro - a[1].micro)[0] ?? [null, null];
|
|
29
|
+
let ahorro = 0;
|
|
30
|
+
for (const t of input.turns) {
|
|
31
|
+
if ((input.from && t.at < input.from) || t.at >= input.to)
|
|
32
|
+
continue;
|
|
33
|
+
const precio = (0, catalog_js_1.resolvePrice)(t.model, t.at);
|
|
34
|
+
if (!precio)
|
|
35
|
+
continue;
|
|
36
|
+
const porToken = precio.inputPerMTok / 1_000_000;
|
|
37
|
+
// Leer de caché cuesta una fracción del precio de entrada; escribir en
|
|
38
|
+
// ella, algo más. El ahorro es lo primero menos lo segundo.
|
|
39
|
+
ahorro += t.cacheRead * porToken * (1 - precio.cacheReadMultiplier)
|
|
40
|
+
- t.cacheWrite5m * porToken * (precio.cacheWrite5mMultiplier - 1)
|
|
41
|
+
- t.cacheWrite1h * porToken * (precio.cacheWrite1hMultiplier - 1);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
from,
|
|
45
|
+
to: new Date(input.to.getTime() - 1),
|
|
46
|
+
measuredSeconds: suma("agent"),
|
|
47
|
+
estimatedSeconds: suma("commit"),
|
|
48
|
+
manualSeconds: suma("manual"),
|
|
49
|
+
aiCost: { microUsd: dentro.reduce((n, e) => n + e.aiCost.microUsd, 0) },
|
|
50
|
+
topProject: pId && pMicro > 0 ? { name: nombre(pId), cost: { microUsd: pMicro } } : null,
|
|
51
|
+
topFeature: rClave && r && r.micro > 0
|
|
52
|
+
? { name: rClave.split("\u0000")[1], project: nombre(r.project), cost: { microUsd: r.micro } }
|
|
53
|
+
: null,
|
|
54
|
+
cacheSavings: { microUsd: Math.max(0, Math.round(ahorro * 1_000_000)) },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const FONDO = [0xE6, 0xEA, 0xE5];
|
|
58
|
+
const PAPEL = [0xFF, 0xFD, 0xF8];
|
|
59
|
+
const TINTA = [0x1E, 0x24, 0x21];
|
|
60
|
+
const TENUE = [0x9A, 0xA2, 0x9D];
|
|
61
|
+
const JADE = [0x0E, 0x7C, 0x6B];
|
|
62
|
+
const S = 3; // escala del texto normal
|
|
63
|
+
const COLS = 34; // caracteres por línea, como un ticket de 80 mm
|
|
64
|
+
const PAD = 44; // margen interior del papel
|
|
65
|
+
const MARGEN = 28; // fondo alrededor del papel
|
|
66
|
+
const DIENTE = 12; // el borde dentado de arriba y abajo
|
|
67
|
+
const LINEA = pixelfont_js_1.GLYPH_H * S + 14;
|
|
68
|
+
function fecha(d) {
|
|
69
|
+
const lang = (0, index_js_1.getLang)();
|
|
70
|
+
return d.toLocaleDateString(lang === "en" ? "en-US" : "es-ES", { day: "numeric", month: "short" })
|
|
71
|
+
.replace(".", "").toUpperCase();
|
|
72
|
+
}
|
|
73
|
+
/** Dólares con dos decimales y separador de miles: "$42,999.71" se lee; "$42999.71" no. */
|
|
74
|
+
function dolares(cost) {
|
|
75
|
+
return "$" + (cost.microUsd / 1_000_000).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
76
|
+
}
|
|
77
|
+
function recortar(texto, max) {
|
|
78
|
+
const letras = [...texto];
|
|
79
|
+
// Sin "…": la fuente no lo tiene, y un punto se entiende igual en un ticket.
|
|
80
|
+
return letras.length <= max ? texto : letras.slice(0, Math.max(1, max - 1)).join("") + ".";
|
|
81
|
+
}
|
|
82
|
+
function filas(d, semana, nombres) {
|
|
83
|
+
const total = d.measuredSeconds + d.estimatedSeconds + d.manualSeconds;
|
|
84
|
+
const rango = `${fecha(d.from)} – ${fecha(d.to)} ${d.to.getFullYear()}`;
|
|
85
|
+
const f = [
|
|
86
|
+
{ t: "titulo", texto: "ESTELA" },
|
|
87
|
+
{ t: "centro", texto: semana ? (0, index_js_1.tr) `RECIBO DE LA SEMANA` : (0, index_js_1.tr) `RECIBO DE TU HISTORIAL` },
|
|
88
|
+
{ t: "centro", texto: rango, color: TENUE },
|
|
89
|
+
{ t: "corte" },
|
|
90
|
+
{ t: "par", izq: (0, index_js_1.tr) `HORAS MEDIDAS`, der: (0, shared_1.formatDuration)(d.measuredSeconds) },
|
|
91
|
+
{ t: "par", izq: (0, index_js_1.tr) `HORAS ESTIMADAS`, der: (0, shared_1.formatDuration)(d.estimatedSeconds) },
|
|
92
|
+
];
|
|
93
|
+
if (d.manualSeconds > 0)
|
|
94
|
+
f.push({ t: "par", izq: (0, index_js_1.tr) `AÑADIDAS A MANO`, der: (0, shared_1.formatDuration)(d.manualSeconds) });
|
|
95
|
+
f.push({ t: "par", izq: (0, index_js_1.tr) `TOTAL`, der: (0, shared_1.formatDuration)(total), negrita: true });
|
|
96
|
+
if (total > 0) {
|
|
97
|
+
const parte = d.measuredSeconds / total;
|
|
98
|
+
f.push({ t: "barra", parte, etiqueta: (0, index_js_1.tr) `${`${Math.round(parte * 100)}%`} MEDIDO` });
|
|
99
|
+
}
|
|
100
|
+
f.push({ t: "corte" });
|
|
101
|
+
if (d.aiCost.microUsd > 0) {
|
|
102
|
+
// La aclaración va en su línea: al lado del importe no cabe cuando pasa de mil.
|
|
103
|
+
f.push({ t: "par", izq: (0, index_js_1.tr) `COSTE DE IA`, der: dolares(d.aiCost), negrita: true });
|
|
104
|
+
f.push({ t: "texto", texto: (0, index_js_1.tr) `(A TARIFA API)`, color: TENUE });
|
|
105
|
+
if (d.cacheSavings.microUsd > 0) {
|
|
106
|
+
f.push({ t: "par", izq: (0, index_js_1.tr) `AHORRO POR CACHÉ`, der: dolares(d.cacheSavings) });
|
|
107
|
+
}
|
|
108
|
+
// Sin nombres salvo que se pidan: un recibo está hecho para enseñarse, y
|
|
109
|
+
// el nombre de un proyecto suele ser el de un cliente. Publicarlo diría
|
|
110
|
+
// para quién trabaja quien lo comparte.
|
|
111
|
+
if (d.topProject) {
|
|
112
|
+
f.push({ t: "hueco" });
|
|
113
|
+
if (nombres) {
|
|
114
|
+
f.push({ t: "texto", texto: (0, index_js_1.tr) `PROYECTO MÁS CARO`, color: TENUE });
|
|
115
|
+
f.push({ t: "par", izq: d.topProject.name, der: dolares(d.topProject.cost), sangria: true });
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
f.push({ t: "par", izq: (0, index_js_1.tr) `PROYECTO MÁS CARO`, der: dolares(d.topProject.cost) });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (d.topFeature) {
|
|
122
|
+
if (nombres) {
|
|
123
|
+
f.push({ t: "texto", texto: (0, index_js_1.tr) `RAMA MÁS CARA`, color: TENUE });
|
|
124
|
+
f.push({ t: "par", izq: d.topFeature.name, der: dolares(d.topFeature.cost), sangria: true });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
f.push({ t: "par", izq: (0, index_js_1.tr) `RAMA MÁS CARA`, der: dolares(d.topFeature.cost) });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
f.push({ t: "centro", texto: (0, index_js_1.tr) `SIN CONSUMO DE IA`, color: TENUE });
|
|
133
|
+
}
|
|
134
|
+
f.push({ t: "corte" }, { t: "centro", texto: (0, index_js_1.tr) `HORAS QUE NO TUVISTE QUE RELLENAR`, color: TENUE }, { t: "hueco" }, { t: "centro", texto: "getestela.dev", color: JADE });
|
|
135
|
+
return f;
|
|
136
|
+
}
|
|
137
|
+
function alto(f) {
|
|
138
|
+
switch (f.t) {
|
|
139
|
+
case "titulo": return pixelfont_js_1.GLYPH_H * 7 + 22;
|
|
140
|
+
case "corte": return 34;
|
|
141
|
+
case "hueco": return 12;
|
|
142
|
+
case "barra": return LINEA + 10;
|
|
143
|
+
default: return LINEA;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** El texto de cada línea del recibo, tal como se pinta. Para los tests. */
|
|
147
|
+
function receiptText(d, options) {
|
|
148
|
+
return filas(d, options.week, options.names === true).flatMap((f) => f.t === "par" ? [`${f.izq} ${f.der}`] : "texto" in f ? [f.texto] : []);
|
|
149
|
+
}
|
|
150
|
+
function renderReceipt(d, options) {
|
|
151
|
+
const lista = filas(d, options.week, options.names === true);
|
|
152
|
+
const anchoTexto = COLS * pixelfont_js_1.ADVANCE * S;
|
|
153
|
+
const anchoPapel = anchoTexto + PAD * 2;
|
|
154
|
+
const altoPapel = PAD + DIENTE + lista.reduce((n, f) => n + alto(f), 0) + PAD;
|
|
155
|
+
const lienzo = new png_js_1.Lienzo(anchoPapel + MARGEN * 2, altoPapel + MARGEN * 2, FONDO);
|
|
156
|
+
// El papel, con los bordes de arriba y abajo en dientes de sierra: el corte
|
|
157
|
+
// de la impresora.
|
|
158
|
+
const x0 = MARGEN, y0 = MARGEN;
|
|
159
|
+
lienzo.rect(x0, y0, anchoPapel, altoPapel, PAPEL);
|
|
160
|
+
for (let x = 0; x < anchoPapel; x++) {
|
|
161
|
+
const h = Math.abs((x % (DIENTE * 2)) - DIENTE);
|
|
162
|
+
lienzo.rect(x0 + x, y0, 1, h, FONDO);
|
|
163
|
+
lienzo.rect(x0 + x, y0 + altoPapel - h, 1, h, FONDO);
|
|
164
|
+
}
|
|
165
|
+
const izq = x0 + PAD;
|
|
166
|
+
let y = y0 + PAD + DIENTE;
|
|
167
|
+
const centrado = (texto, escala) => x0 + Math.round((anchoPapel - (0, pixelfont_js_1.textWidth)(texto, escala)) / 2);
|
|
168
|
+
for (const f of lista) {
|
|
169
|
+
switch (f.t) {
|
|
170
|
+
case "titulo":
|
|
171
|
+
(0, pixelfont_js_1.drawText)(lienzo, f.texto, centrado(f.texto, 7), y, 7, JADE, true);
|
|
172
|
+
break;
|
|
173
|
+
case "centro": {
|
|
174
|
+
const texto = recortar(f.texto, COLS);
|
|
175
|
+
(0, pixelfont_js_1.drawText)(lienzo, texto, centrado(texto, S), y, S, f.color ?? TINTA);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
case "texto":
|
|
179
|
+
(0, pixelfont_js_1.drawText)(lienzo, recortar(f.texto, COLS), izq, y, S, f.color ?? TINTA);
|
|
180
|
+
break;
|
|
181
|
+
case "par": {
|
|
182
|
+
const sangria = f.sangria ? 2 : 0;
|
|
183
|
+
const der = f.der;
|
|
184
|
+
const max = COLS - sangria - [...der].length - 2;
|
|
185
|
+
const etiqueta = recortar(f.izq, max);
|
|
186
|
+
(0, pixelfont_js_1.drawText)(lienzo, etiqueta, izq + sangria * pixelfont_js_1.ADVANCE * S, y, S, TINTA, f.negrita);
|
|
187
|
+
(0, pixelfont_js_1.drawText)(lienzo, der, izq + anchoTexto - (0, pixelfont_js_1.textWidth)(der, S), y, S, TINTA, f.negrita);
|
|
188
|
+
// Puntos de relleno entre la etiqueta y el importe, como en un ticket.
|
|
189
|
+
const desde = sangria + [...etiqueta].length + 1;
|
|
190
|
+
const hasta = COLS - [...der].length - 1;
|
|
191
|
+
for (let c = desde; c < hasta; c++) {
|
|
192
|
+
lienzo.rect(izq + c * pixelfont_js_1.ADVANCE * S + 2 * S, y + 8 * S, S, S, TENUE);
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case "barra": {
|
|
197
|
+
const ancho = anchoTexto - (0, pixelfont_js_1.textWidth)(f.etiqueta, S) - pixelfont_js_1.ADVANCE * S;
|
|
198
|
+
const lleno = Math.round(ancho * Math.min(1, Math.max(0, f.parte)));
|
|
199
|
+
const by = y + 3 * S;
|
|
200
|
+
lienzo.rect(izq, by, ancho, 5 * S, [0xE4, 0xE8, 0xE3]);
|
|
201
|
+
lienzo.rect(izq, by, lleno, 5 * S, JADE);
|
|
202
|
+
(0, pixelfont_js_1.drawText)(lienzo, f.etiqueta, izq + anchoTexto - (0, pixelfont_js_1.textWidth)(f.etiqueta, S), y, S, JADE);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
case "corte":
|
|
206
|
+
for (let x = 0; x < anchoTexto; x += 12)
|
|
207
|
+
lienzo.rect(izq + x, y + 15, 6, 2, TENUE);
|
|
208
|
+
break;
|
|
209
|
+
case "hueco":
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
y += alto(f);
|
|
213
|
+
}
|
|
214
|
+
return lienzo.png();
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=receipt.js.map
|