Skip to content

Commit a381a48

Browse files
authored
Refactor block box (#47)
1 parent a6a7491 commit a381a48

File tree

6 files changed

+174
-148
lines changed

6 files changed

+174
-148
lines changed
126 Bytes
Loading
-2.29 KB
Loading
-372 Bytes
Loading

src/__snapshots__/index.spec.ts.snap

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -545,13 +545,9 @@ exports[`e2e > makurano 2`] = `
545545
exports[`e2e > math 2`] = `
546546
"Lift(L) can be determined by Lift Coefficient (C_L) like the following
547547
equation.
548-
L = \\frac{1}{2} \\rho v^2 S C_L
549-
i hbar \\frac{partial psi}{partial t} = H psi(x,t)
550-
\\f\\relax{x} = \\int_{-\\infty}^\\infty\\f\\hat\\xi\\,e^{2 \\pi i \\xi x}\\,d\\xi
551-
\\frac{a+b}{c+d}
552-
\\sqrt[30]{a+\\sqrt{b+c}}
553-
\\sum_{k=1}^{n} k=\\frac{n(n+1)}{2}
554-
\\alpha\\beta\\gamma\\delta\\epsilon\\zeta\\eta\\theta\\iota\\kappa\\lambda\\mu\\nu\\xi
548+
L = \\frac{1}{2} \\rho v^2 S C_Li hbar \\frac{partial psi}{partial t} = H psi(x,t)\\f\\relax{x} = \\int_{-\\infty}
549+
^\\infty\\f\\hat\\xi\\,e^{2 \\pi i \\xi x}\\,d\\xi\\frac{a+b}{c+d}\\sqrt[30]{a+\\sqrt{b+c}}\\sum_{k=1}^{n}
550+
k=\\frac{n(n+1)}{2} \\alpha\\beta\\gamma\\delta\\epsilon\\zeta\\eta\\theta\\iota\\kappa\\lambda\\mu\\nu\\xi
555551
o\\pi\\rho\\sigma\\tau\\upsilon\\phi\\chi\\psi\\omega
556552
\\varepsilon\\vartheta\\varrho\\varsigma\\varphi
557553
\\Gamma\\Delta\\Theta\\Lambda\\Xi\\Pi\\Sigma\\Upsilon\\Phi\\Psi\\Omega"
@@ -583,9 +579,7 @@ test"
583579
exports[`e2e > tag 2`] = `
584580
"<div>
585581
<span>This is <b>html</b></span> inside <u>docx</u>.
586-
</div>
587-
<table><tr><td>aaaaa</td></tr></table>
588-
<!--comment-->
582+
</div><table><tr><td>aaaaa</td></tr></table><!--comment-->
589583
mailto:foobarbaz
590584
aaaaaa"
591585
`;

src/layout.ts

Lines changed: 132 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ interface Box {
2323

2424
export interface BlockBox extends Box {
2525
readonly type: "block";
26+
readonly border: boolean;
27+
readonly children: LayoutBox[];
2628
}
2729
export interface TextBox extends Box {
2830
readonly type: "text";
@@ -48,8 +50,8 @@ export type ResolveImageSize = (src: string) => {
4850
} | null;
4951

5052
interface Options {
51-
readonly contentTop: number;
52-
readonly contentWidth: number;
53+
readonly top: number;
54+
readonly width: number;
5355
readonly spacing?: number;
5456
readonly textWidth: TextWidth;
5557
readonly textHeight: TextHeight;
@@ -62,148 +64,149 @@ export const layoutBlock = (
6264
startX: number,
6365
startY: number,
6466
options: Options,
65-
): LayoutBox[] => {
67+
): BlockBox[] => {
6668
const {
67-
contentTop,
68-
contentWidth,
69+
top,
70+
width,
6971
spacing,
7072
textWidth,
7173
textHeight,
7274
resolveFont,
7375
resolveImageSize,
7476
} = options;
75-
const result: LayoutBox[] = [];
76-
const inlines = children.filter(
77-
(c) => c.type === "text" || c.type === "void",
78-
);
79-
let y = max(startY, contentTop);
80-
if (children.length !== inlines.length) {
81-
let afterPagebreak = false;
82-
for (let i = 0; i < children.length; i++) {
83-
const node = children[i]!;
84-
switch (node.type) {
85-
case "text":
86-
case "void": {
87-
if (afterPagebreak) {
88-
y = contentTop;
89-
afterPagebreak = false;
90-
}
91-
const childBoxes = layoutBlock(
92-
{
93-
type: "block",
94-
style: { display: "block" },
95-
children: [node],
96-
},
97-
startX,
98-
y,
99-
options,
100-
);
101-
if (childBoxes.length > 0) {
102-
result.push(...childBoxes);
103-
const lastBox = childBoxes[childBoxes.length - 1]!;
104-
if ("height" in lastBox) {
105-
y = lastBox.y + lastBox.height;
106-
}
107-
}
108-
break;
109-
}
110-
case "block": {
111-
if (afterPagebreak) {
112-
y = contentTop;
113-
afterPagebreak = false;
114-
}
115-
let childBoxes: LayoutBox[] = [];
116-
if (node.style && node.style.display === "table") {
117-
const rows = node.children.filter((c) => c.type === "block");
118-
if (!rows[0]) break;
119-
const colCount = rows[0].children.length;
120-
const cellWidth = contentWidth / colCount;
121-
const cellPadding = 2;
122-
let tableY = y;
123-
for (const row of rows) {
124-
const cells = row.children.filter((c) => c.type === "block");
125-
let cellHeight = 0;
126-
const cellBoxes: LayoutBox[][] = [];
127-
for (let colIdx = 0; colIdx < cells.length; colIdx++) {
128-
const cell = cells[colIdx]!;
129-
const boxes = measureInlines(
130-
cell.children.filter(
131-
(n) => n.type === "text" || n.type === "void",
132-
),
133-
{
134-
x: startX + colIdx * cellWidth + cellPadding,
135-
y: tableY + cellPadding * 2,
136-
align: cell.style.textAlign,
137-
width: cellWidth - cellPadding * 2,
138-
},
139-
textWidth,
140-
textHeight,
141-
resolveFont,
142-
resolveImageSize,
143-
);
144-
cellBoxes.push(boxes);
145-
const maxCellBottom = boxes.reduce(
146-
(acc, b) => max(acc, b.y + b.height),
147-
tableY,
148-
);
149-
cellHeight = max(cellHeight, maxCellBottom - tableY);
150-
}
151-
for (const boxes of cellBoxes) {
152-
childBoxes.push(...boxes);
153-
}
154-
for (let colIdx = 0; colIdx < cells.length; colIdx++) {
155-
childBoxes.push({
156-
type: "block",
157-
x: startX + colIdx * cellWidth,
158-
y: tableY,
159-
width: cellWidth,
160-
height: cellHeight,
161-
});
162-
}
163-
tableY += cellHeight;
164-
}
165-
y = tableY;
166-
} else {
167-
childBoxes = layoutBlock(node, startX, y, options);
168-
const lastBox = childBoxes[childBoxes.length - 1]!;
169-
if ("height" in lastBox) {
170-
y = lastBox.y + lastBox.height;
171-
}
172-
}
173-
if (childBoxes.length > 0) {
174-
result.push(...childBoxes);
175-
}
176-
if (spacing) {
177-
y += spacing;
178-
}
179-
break;
180-
}
181-
case "pagebreak": {
182-
result.push({ type: "pagebreak" });
183-
afterPagebreak = true;
184-
break;
185-
}
186-
default: {
187-
node satisfies never;
188-
}
189-
}
190-
}
191-
} else {
192-
const boxes = measureInlines(
193-
inlines,
77+
let y = max(startY, top);
78+
let height = 0;
79+
let border = false;
80+
const boxes: LayoutBox[] = [];
81+
82+
if (style.display === "table-cell") {
83+
border = true;
84+
const cellPadding = 2;
85+
const inlineBoxes = measureInlines(
86+
children.filter((c) => c.type === "text" || c.type === "void"),
19487
{
195-
x: startX + (style.indent ?? 0),
196-
y: y,
197-
width: contentWidth,
88+
x: startX + cellPadding,
89+
y: y + cellPadding,
90+
align: style.textAlign,
91+
width: width - cellPadding * 2,
19892
},
19993
textWidth,
20094
textHeight,
20195
resolveFont,
20296
resolveImageSize,
20397
);
204-
result.push(...boxes);
98+
boxes.push(...inlineBoxes);
99+
height =
100+
inlineBoxes.reduce((acc, b) => max(acc, b.y + b.height - y), 0) +
101+
cellPadding;
102+
} else if (style.display === "table") {
103+
const rows = children.filter((c) => c.type === "block");
104+
if (rows[0]) {
105+
const colCount = rows[0].children.length;
106+
const cellWidth = width / colCount;
107+
let tableY = y;
108+
for (const row of rows) {
109+
const cells = row.children.filter((c) => c.type === "block");
110+
let rowHeight = 0;
111+
const rowChildren: BlockBox[] = [];
112+
for (let colIdx = 0; colIdx < cells.length; colIdx++) {
113+
const cell = cells[colIdx]!;
114+
const cellBoxes = layoutBlock(
115+
cell,
116+
startX + colIdx * cellWidth,
117+
tableY,
118+
{ ...options, width: cellWidth },
119+
);
120+
rowChildren.push(...cellBoxes);
121+
rowHeight = max(
122+
rowHeight,
123+
cellBoxes.reduce((acc, b) => max(acc, b.height), 0),
124+
);
125+
}
126+
boxes.push({
127+
type: "block",
128+
x: startX,
129+
y: tableY,
130+
width: width,
131+
height: rowHeight,
132+
border: false,
133+
children: rowChildren,
134+
});
135+
tableY += rowHeight;
136+
}
137+
height = tableY - y;
138+
}
139+
} else if (style.display === "block" || style.display === "table-row") {
140+
let afterPagebreak = false;
141+
const inlineBuffer: (TextNode | VoidNode)[] = [];
142+
143+
const flush = () => {
144+
if (inlineBuffer.length > 0) {
145+
const inlineBoxes = measureInlines(
146+
inlineBuffer,
147+
{
148+
x: startX + (style.indent ?? 0),
149+
y: y,
150+
width: width,
151+
},
152+
textWidth,
153+
textHeight,
154+
resolveFont,
155+
resolveImageSize,
156+
);
157+
boxes.push(...inlineBoxes);
158+
if (inlineBoxes.length > 0) {
159+
const lastBox = inlineBoxes[inlineBoxes.length - 1]!;
160+
y = lastBox.y + lastBox.height;
161+
}
162+
inlineBuffer.splice(0);
163+
}
164+
};
165+
166+
for (const node of children) {
167+
if (node.type === "pagebreak") {
168+
flush();
169+
boxes.push({ type: "pagebreak" });
170+
afterPagebreak = true;
171+
continue;
172+
}
173+
if (afterPagebreak) {
174+
y = top;
175+
afterPagebreak = false;
176+
}
177+
if (node.type === "block") {
178+
flush();
179+
const childBoxes = layoutBlock(node, startX, y, options);
180+
boxes.push(...childBoxes);
181+
if (childBoxes.length > 0) {
182+
const lastBox = childBoxes[childBoxes.length - 1]!;
183+
y = lastBox.y + lastBox.height;
184+
}
185+
} else if (node.type === "text" || node.type === "void") {
186+
inlineBuffer.push(node);
187+
}
188+
if (spacing) {
189+
if (node.type === "block") {
190+
y += spacing;
191+
}
192+
}
193+
}
194+
flush();
195+
196+
height = y - startY;
205197
}
206-
return result;
198+
199+
return [
200+
{
201+
type: "block",
202+
x: startX,
203+
y: startY,
204+
width: width,
205+
height,
206+
border,
207+
children: boxes,
208+
},
209+
];
207210
};
208211

209212
const measureInlines = (

0 commit comments

Comments
 (0)