@thi.ng/text-canvas 2.6.22 → 2.6.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/api.js +95 -79
- package/bars.js +37 -31
- package/canvas.js +164 -135
- package/circle.js +55 -66
- package/format.js +14 -20
- package/hvline.js +64 -61
- package/image.js +231 -310
- package/line.js +66 -73
- package/package.json +15 -12
- package/rect.js +66 -88
- package/style.js +30 -26
- package/table.js +133 -118
- package/text.js +86 -108
- package/utils.js +13 -8
package/table.js
CHANGED
|
@@ -2,132 +2,147 @@ import { peek } from "@thi.ng/arrays/peek";
|
|
|
2
2
|
import { isString } from "@thi.ng/checks/is-string";
|
|
3
3
|
import { wordWrapLines } from "@thi.ng/strings/word-wrap";
|
|
4
4
|
import { Border } from "./api.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
beginClip,
|
|
7
|
+
beginStyle,
|
|
8
|
+
canvas,
|
|
9
|
+
endClip,
|
|
10
|
+
endStyle,
|
|
11
|
+
setAt
|
|
12
|
+
} from "./canvas.js";
|
|
6
13
|
import { hline, vline } from "./hvline.js";
|
|
7
14
|
import { fillRect, strokeRect } from "./rect.js";
|
|
8
15
|
import { horizontalOnly, verticalOnly } from "./style.js";
|
|
9
16
|
import { textLines } from "./text.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
wrapped.push(wrappedRow);
|
|
17
|
+
const initTable = (opts, cells) => {
|
|
18
|
+
const b = opts.border !== void 0 ? opts.border : Border.ALL;
|
|
19
|
+
const bH = b & Border.H ? 1 : 0;
|
|
20
|
+
const bV = b & Border.V ? 1 : 0;
|
|
21
|
+
const bF = bH && bV || b & Border.FRAME ? 1 : 0;
|
|
22
|
+
const bFH = bF | bH;
|
|
23
|
+
const bFV = bF | bV;
|
|
24
|
+
const [padH, padV] = (opts.padding || [0, 0]).map((x) => x << 1);
|
|
25
|
+
const cols = opts.cols;
|
|
26
|
+
const numCols = cols.length - 1;
|
|
27
|
+
const numRows = cells.length - 1;
|
|
28
|
+
const rowHeights = new Array(numRows + 1).fill(0);
|
|
29
|
+
const wrapped = [];
|
|
30
|
+
for (let i = 0; i <= numRows; i++) {
|
|
31
|
+
const row = cells[i];
|
|
32
|
+
const wrappedRow = [];
|
|
33
|
+
for (let j = 0; j <= numCols; j++) {
|
|
34
|
+
const cell = isString(row[j]) ? { body: row[j] } : row[j];
|
|
35
|
+
const lines = cell.wrap !== false ? wordWrapLines(cell.body, {
|
|
36
|
+
width: cols[j].width,
|
|
37
|
+
hard: cell.hard || opts.hard
|
|
38
|
+
}).map((l) => l.toString()) : cell.body.split(/\r?\n/g);
|
|
39
|
+
wrappedRow.push({
|
|
40
|
+
body: lines,
|
|
41
|
+
format: cell.format
|
|
42
|
+
});
|
|
43
|
+
rowHeights[i] = Math.max(
|
|
44
|
+
rowHeights[i],
|
|
45
|
+
lines.length,
|
|
46
|
+
cell.height || 0
|
|
47
|
+
);
|
|
43
48
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
bV,
|
|
66
|
-
bFH,
|
|
67
|
-
bFV,
|
|
68
|
-
};
|
|
49
|
+
wrapped.push(wrappedRow);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
style: opts.style,
|
|
53
|
+
format: opts.format,
|
|
54
|
+
formatHead: opts.formatHead,
|
|
55
|
+
width: cols.reduce((acc, x) => acc + x.width, 0) + 2 * bFV + numCols * bV + (numCols + 1) * padH,
|
|
56
|
+
height: rowHeights.reduce((acc, x) => acc + x, 0) + 2 * bFH + numRows * bH + (numRows + 1) * padV,
|
|
57
|
+
cells: wrapped,
|
|
58
|
+
rowHeights,
|
|
59
|
+
cols,
|
|
60
|
+
numCols,
|
|
61
|
+
numRows,
|
|
62
|
+
padH,
|
|
63
|
+
padV,
|
|
64
|
+
b,
|
|
65
|
+
bH,
|
|
66
|
+
bV,
|
|
67
|
+
bFH,
|
|
68
|
+
bFV
|
|
69
|
+
};
|
|
69
70
|
};
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
71
|
+
const drawTable = (canvas2, x, y, opts) => {
|
|
72
|
+
const {
|
|
73
|
+
cells,
|
|
74
|
+
cols,
|
|
75
|
+
numCols,
|
|
76
|
+
numRows,
|
|
77
|
+
rowHeights,
|
|
78
|
+
width,
|
|
79
|
+
height,
|
|
80
|
+
padH,
|
|
81
|
+
padV,
|
|
82
|
+
bH,
|
|
83
|
+
bV,
|
|
84
|
+
bFH,
|
|
85
|
+
bFV
|
|
86
|
+
} = opts;
|
|
87
|
+
const fmt = opts.format !== void 0 ? opts.format : canvas2.format;
|
|
88
|
+
const fmtHd = opts.formatHead !== void 0 ? opts.formatHead : fmt;
|
|
89
|
+
const currFormat = canvas2.format;
|
|
90
|
+
canvas2.format = fmt;
|
|
91
|
+
let style = opts.style || peek(canvas2.styles);
|
|
92
|
+
style = opts.b === Border.H ? horizontalOnly(style) : opts.b === Border.V ? verticalOnly(style) : style;
|
|
93
|
+
beginStyle(canvas2, style);
|
|
94
|
+
fillRect(canvas2, x + bFV, y + bFH, width - 2 * bFV, height - 2 * bFH, " ");
|
|
95
|
+
opts.b && strokeRect(canvas2, x, y, width, height);
|
|
96
|
+
if (bV) {
|
|
97
|
+
for (let i = 1, xx = x + cols[0].width + padH + 1; i <= numCols; xx += cols[i].width + padH + 1, i++) {
|
|
98
|
+
vline(canvas2, xx, y, height, style.tjt, style.tjb, style.vl);
|
|
90
99
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (let j = 0, xx = x + bFV; j <= numCols; j++) {
|
|
99
|
-
const col = cols[j];
|
|
100
|
-
const curr = row[j];
|
|
101
|
-
if (curr.body) {
|
|
102
|
-
beginClip(canvas, xx, yy, col.width + padH, rowH + padV);
|
|
103
|
-
textLines(canvas, xx + padH / 2, yy + padV / 2, curr.body, curr.format || (i ? fmt : fmtHd));
|
|
104
|
-
endClip(canvas);
|
|
105
|
-
}
|
|
106
|
-
if (bH && bV && j > 0 && i < numRows) {
|
|
107
|
-
setAt(canvas, xx - 1, y2, style.jct);
|
|
108
|
-
}
|
|
109
|
-
xx += col.width + bV + padH;
|
|
110
|
-
}
|
|
111
|
-
yy = y2 + bH;
|
|
100
|
+
}
|
|
101
|
+
for (let i = 0, yy = y + bFH; i <= numRows; i++) {
|
|
102
|
+
const row = cells[i];
|
|
103
|
+
const rowH = rowHeights[i];
|
|
104
|
+
const y2 = yy + rowH + padV;
|
|
105
|
+
if (bH && i < numRows) {
|
|
106
|
+
hline(canvas2, x, y2, width, style.tjl, style.tjr, style.hl);
|
|
112
107
|
}
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
for (let j = 0, xx = x + bFV; j <= numCols; j++) {
|
|
109
|
+
const col = cols[j];
|
|
110
|
+
const curr = row[j];
|
|
111
|
+
if (curr.body) {
|
|
112
|
+
beginClip(canvas2, xx, yy, col.width + padH, rowH + padV);
|
|
113
|
+
textLines(
|
|
114
|
+
canvas2,
|
|
115
|
+
xx + padH / 2,
|
|
116
|
+
yy + padV / 2,
|
|
117
|
+
curr.body,
|
|
118
|
+
curr.format || (i ? fmt : fmtHd)
|
|
119
|
+
);
|
|
120
|
+
endClip(canvas2);
|
|
121
|
+
}
|
|
122
|
+
if (bH && bV && j > 0 && i < numRows) {
|
|
123
|
+
setAt(canvas2, xx - 1, y2, style.jct);
|
|
124
|
+
}
|
|
125
|
+
xx += col.width + bV + padH;
|
|
126
|
+
}
|
|
127
|
+
yy = y2 + bH;
|
|
128
|
+
}
|
|
129
|
+
endStyle(canvas2);
|
|
130
|
+
canvas2.format = currFormat;
|
|
131
|
+
};
|
|
132
|
+
const table = (canvas2, x, y, opts, cells) => {
|
|
133
|
+
const spec = initTable(opts, cells);
|
|
134
|
+
drawTable(canvas2, x, y, spec);
|
|
135
|
+
return [spec.width, spec.height];
|
|
115
136
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
137
|
+
const tableCanvas = (opts, cells) => {
|
|
138
|
+
const tbl = initTable(opts, cells);
|
|
139
|
+
const result = canvas(tbl.width, tbl.height);
|
|
140
|
+
drawTable(result, 0, 0, tbl);
|
|
141
|
+
return result;
|
|
120
142
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
* @param cells - table cells (row major)
|
|
127
|
-
*/
|
|
128
|
-
export const tableCanvas = (opts, cells) => {
|
|
129
|
-
const tbl = initTable(opts, cells);
|
|
130
|
-
const result = canvas(tbl.width, tbl.height);
|
|
131
|
-
drawTable(result, 0, 0, tbl);
|
|
132
|
-
return result;
|
|
143
|
+
export {
|
|
144
|
+
drawTable,
|
|
145
|
+
initTable,
|
|
146
|
+
table,
|
|
147
|
+
tableCanvas
|
|
133
148
|
};
|
package/text.js
CHANGED
|
@@ -1,116 +1,94 @@
|
|
|
1
1
|
import { peek } from "@thi.ng/arrays/peek";
|
|
2
2
|
import { clamp0 } from "@thi.ng/math/interval";
|
|
3
3
|
import { wordWrapLines } from "@thi.ng/strings/word-wrap";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
beginClip,
|
|
6
|
+
beginStyle,
|
|
7
|
+
endClip,
|
|
8
|
+
endStyle
|
|
9
|
+
} from "./canvas.js";
|
|
5
10
|
import { fillRect, strokeRect } from "./rect.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (x < x1) {
|
|
24
|
-
i = x1 - x;
|
|
25
|
-
x = x1;
|
|
26
|
-
}
|
|
27
|
-
const { data, width } = canvas;
|
|
28
|
-
const n = line.length;
|
|
29
|
-
format <<= 16;
|
|
30
|
-
for (let idx = x + y * width; i < n && x < x2; i++, x++, idx++) {
|
|
31
|
-
data[idx] = line.charCodeAt(i) | format;
|
|
32
|
-
}
|
|
11
|
+
const textLine = (canvas, x, y, line, format = canvas.format) => {
|
|
12
|
+
x |= 0;
|
|
13
|
+
y |= 0;
|
|
14
|
+
const { x1, y1, x2, y2 } = peek(canvas.clipRects);
|
|
15
|
+
if (y < y1 || y >= y2 || x >= x2)
|
|
16
|
+
return;
|
|
17
|
+
let i = 0;
|
|
18
|
+
if (x < x1) {
|
|
19
|
+
i = x1 - x;
|
|
20
|
+
x = x1;
|
|
21
|
+
}
|
|
22
|
+
const { data, width } = canvas;
|
|
23
|
+
const n = line.length;
|
|
24
|
+
format <<= 16;
|
|
25
|
+
for (let idx = x + y * width; i < n && x < x2; i++, x++, idx++) {
|
|
26
|
+
data[idx] = line.charCodeAt(i) | format;
|
|
27
|
+
}
|
|
33
28
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
const textLines = (canvas, x, y, lines, format = canvas.format) => {
|
|
30
|
+
for (let line of lines) {
|
|
31
|
+
textLine(canvas, x, y, line, format);
|
|
32
|
+
y++;
|
|
33
|
+
}
|
|
34
|
+
return y;
|
|
40
35
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*/
|
|
54
|
-
export const textColumn = (canvas, x, y, width, txt, format = canvas.format, hard = false) => {
|
|
55
|
-
x |= 0;
|
|
56
|
-
y |= 0;
|
|
57
|
-
width |= 0;
|
|
58
|
-
const height = canvas.height;
|
|
59
|
-
for (let line of wordWrapLines(txt, { width, hard })) {
|
|
60
|
-
textLine(canvas, x, y, line.toString(), format);
|
|
61
|
-
y++;
|
|
62
|
-
if (y >= height)
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
return y;
|
|
36
|
+
const textColumn = (canvas, x, y, width, txt, format = canvas.format, hard = false) => {
|
|
37
|
+
x |= 0;
|
|
38
|
+
y |= 0;
|
|
39
|
+
width |= 0;
|
|
40
|
+
const height = canvas.height;
|
|
41
|
+
for (let line of wordWrapLines(txt, { width, hard })) {
|
|
42
|
+
textLine(canvas, x, y, line.toString(), format);
|
|
43
|
+
y++;
|
|
44
|
+
if (y >= height)
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
return y;
|
|
66
48
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
endClip(canvas);
|
|
113
|
-
style && endStyle(canvas);
|
|
114
|
-
canvas.format = currFmt;
|
|
115
|
-
return y + height;
|
|
49
|
+
const textBox = (canvas, x, y, width, height, txt, opts) => {
|
|
50
|
+
const {
|
|
51
|
+
format,
|
|
52
|
+
style,
|
|
53
|
+
padding: [padX, padY],
|
|
54
|
+
hard
|
|
55
|
+
} = {
|
|
56
|
+
format: canvas.format,
|
|
57
|
+
padding: [0, 0],
|
|
58
|
+
hard: false,
|
|
59
|
+
...opts
|
|
60
|
+
};
|
|
61
|
+
const currFmt = canvas.format;
|
|
62
|
+
canvas.format = format;
|
|
63
|
+
style && beginStyle(canvas, style);
|
|
64
|
+
x |= 0;
|
|
65
|
+
y |= 0;
|
|
66
|
+
width |= 0;
|
|
67
|
+
let innerW = width - 2 - 2 * padX;
|
|
68
|
+
let innerH = 0;
|
|
69
|
+
const lines = wordWrapLines(txt, { width: innerW, hard }).map(
|
|
70
|
+
(l) => l.toString()
|
|
71
|
+
);
|
|
72
|
+
if (height < 0) {
|
|
73
|
+
innerH = lines.length + 2;
|
|
74
|
+
height = innerH + 2 * padY;
|
|
75
|
+
} else {
|
|
76
|
+
innerH = clamp0(height - 2);
|
|
77
|
+
}
|
|
78
|
+
strokeRect(canvas, x, y, width, height);
|
|
79
|
+
fillRect(canvas, x + 1, y + 1, width - 2, height - 2, " ");
|
|
80
|
+
x += 1 + padX;
|
|
81
|
+
y += 1 + padY;
|
|
82
|
+
beginClip(canvas, x, y, innerW, innerH);
|
|
83
|
+
y = textLines(canvas, x, y, lines);
|
|
84
|
+
endClip(canvas);
|
|
85
|
+
style && endStyle(canvas);
|
|
86
|
+
canvas.format = currFmt;
|
|
87
|
+
return y + height;
|
|
88
|
+
};
|
|
89
|
+
export {
|
|
90
|
+
textBox,
|
|
91
|
+
textColumn,
|
|
92
|
+
textLine,
|
|
93
|
+
textLines
|
|
116
94
|
};
|
package/utils.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const charCode = (x, format) => (typeof x === "string" ? x.charCodeAt(0) : x) | format << 16;
|
|
2
|
+
const intersectRect = (a, b) => {
|
|
3
|
+
const x1 = Math.max(a.x1, b.x1);
|
|
4
|
+
const y1 = Math.max(a.y1, b.y1);
|
|
5
|
+
const x2 = Math.min(a.x2, b.x2);
|
|
6
|
+
const y2 = Math.min(a.y2, b.y2);
|
|
7
|
+
return { x1, y1, x2, y2, w: Math.max(x2 - x1, 0), h: Math.max(y2 - y1, 0) };
|
|
8
8
|
};
|
|
9
9
|
const axis = (a, b, c) => (a < b ? a - b : a > b + c ? a - b - c : 0) ** 2;
|
|
10
|
-
|
|
10
|
+
const intersectRectCircle = (x, y, w, h, cx, cy, r) => axis(cx, x, w) + axis(cy, y, h) <= r * r;
|
|
11
|
+
export {
|
|
12
|
+
charCode,
|
|
13
|
+
intersectRect,
|
|
14
|
+
intersectRectCircle
|
|
15
|
+
};
|