Skip to content

Commit

Permalink
feat(text-canvas): add table support & options
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Feb 17, 2020
1 parent dc1cb05 commit 8983ad6
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 0 deletions.
29 changes: 29 additions & 0 deletions packages/text-canvas/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
import { Fn } from "@thi.ng/api";

export const enum Align {
LEFT,
RIGHT,
CENTER
}

export const enum Border {
NONE = 0,
H = 1,
V = 2,
ALL = 3,
FRAME = 4
}

export interface TableOpts {
cols: { width: number /* align?: Align*/ }[];
style?: StrokeStyle;
format?: number;
formatHead?: number;
border?: Border;
padding?: number[];
}

export interface TextBoxOpts {
format: number;
padding: number[];
style: StrokeStyle;
}

export interface ClipRect {
x1: number;
y1: number;
Expand Down
1 change: 1 addition & 0 deletions packages/text-canvas/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export * from "./hvline";
export * from "./line";
export * from "./rect";
export * from "./string";
export * from "./table";
export * from "./text";
export * from "./utils";
156 changes: 156 additions & 0 deletions packages/text-canvas/src/table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { peek } from "@thi.ng/arrays";
import { Border, TableOpts } from "./api";
import {
beginClip,
beginStyle,
Canvas,
endClip,
endStyle,
setAt
} from "./canvas";
import { hline, vline } from "./hvline";
import { fillRect, strokeRect } from "./rect";
import { horizontalOnly, verticalOnly } from "./style";
import { textLines, wordWrappedLines } from "./text";

export const initTable = (opts: TableOpts, cells: string[][]) => {
const b = opts.border !== undefined ? opts.border : Border.ALL;
const bH = b & Border.H ? 1 : 0;
const bV = b & Border.V ? 1 : 0;
const bF = (bH && bV) || b & Border.FRAME ? 1 : 0;
const bFH = bF | bH;
const bFV = bF | bV;

const [padH, padV] = (opts.padding || [0, 0]).map((x) => x << 1);
const cols = opts.cols;
const numCols = cols.length - 1;
const numRows = cells.length - 1;
const rowHeights = new Array<number>(numRows + 1).fill(0);
const wrapped: string[][][] = [];
for (let i = 0; i <= numRows; i++) {
const row = cells[i];
const wrappedRow: string[][] = [];
for (let j = 0; j < row.length; j++) {
const lines = wordWrappedLines(cols[j].width, row[j]);
wrappedRow.push(lines);
rowHeights[i] = Math.max(rowHeights[i], lines.length);
}
wrapped.push(wrappedRow);
}
return {
style: opts.style,
format: opts.format,
formatHead: opts.formatHead,
width:
cols.reduce((acc, x) => acc + x.width, 0) +
2 * bFV +
numCols * bV +
(numCols + 1) * padH,
height:
rowHeights.reduce((acc, x) => acc + x, 0) +
2 * bFH +
numRows * bH +
(numRows + 1) * padV,
cells: wrapped,
rowHeights,
cols,
numCols,
numRows,
padH,
padV,
b,
bH,
bV,
bFH,
bFV
};
};

export const drawTable = (
canvas: Canvas,
x: number,
y: number,
opts: ReturnType<typeof initTable>
) => {
const {
cells,
cols,
numCols,
numRows,
rowHeights,
width,
height,
padH,
padV,
bH,
bV,
bFH,
bFV
} = opts;
const fmt = opts.format !== undefined ? opts.format : canvas.format;
const fmtHd =
opts.formatHead !== undefined ? opts.formatHead : canvas.format;
const currFormat = canvas.format;
canvas.format = fmt;
let style = opts.style || peek(canvas.styles);
style =
opts.b === Border.H
? horizontalOnly(style)
: opts.b === Border.V
? verticalOnly(style)
: style;

beginStyle(canvas, style);
fillRect(canvas, x + bFV, y + bFH, width - 2 * bFV, height - 2 * bFH, " ");
opts.b && strokeRect(canvas, x, y, width, height);
if (bV) {
for (
let i = 1, xx = x + cols[0].width + padH + 1;
i <= numCols;
xx += cols[i].width + padH + 1, i++
) {
vline(canvas, xx, y, height, style.tjt, style.tjb, style.vl);
}
}

for (let i = 0, yy = y + bFH; i <= numRows; i++) {
const row = cells[i];
const rowH = rowHeights[i];
const y2 = yy + rowH + padV;
if (bH && i < numRows) {
hline(canvas, x, y2, width, style.tjl, style.tjr, style.hl);
}
for (let j = 0, xx = x + bFV; j <= numCols; j++) {
const col = cols[j];
beginClip(canvas, xx, yy, col.width + padH, rowH + padV);
textLines(
canvas,
xx + padH / 2,
yy + padV / 2,
row[j] || [],
i ? fmt : fmtHd
);
endClip(canvas);
if (bH && bV && j > 0 && i < numRows) {
setAt(canvas, xx - 1, y2, style.jct);
}
xx += col.width + bV + padH;
}
yy = y2 + bH;
}

endStyle(canvas);
canvas.format = currFormat;
};

export const table = (
canvas: Canvas,
x: number,
y: number,
opts: TableOpts,
cells: string[][]
) => {
const spec = initTable(opts, cells);
drawTable(canvas, x, y, spec);
return [spec.width, spec.height];
};

0 comments on commit 8983ad6

Please sign in to comment.