Commit 903a2b52 authored by Guillaume's avatar Guillaume

resolves #245 ignore break-after rule

parent 7500da5a
Pipeline #700 failed with stage
in 23 seconds
<!DOCTYPE html PUBLIC>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>
Long table
</title>
<script src="../../../dist/paged.polyfill.js"></script>
<style>
@page {
size: 140mm 120mm;
margin: 0;
padding: 0;
}
h2 {
font-size: 2em;
break-after: avoid;
}
table {
break-inside: avoid;
}
@media screen {
body {
background-color: whitesmoke;
}
.pagedjs_page {
background-color: white;
box-shadow: 0 0 0 1px #bfbfbf;
margin-top: 10mm;
margin-left: 10mm;
}
}
</style>
</head>
<body>
<section>
<h2>Table</h2>
<div class="content">
<table>
<thead>
<tr>
<th>#</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>a</td>
</tr>
<tr>
<td>1</td>
<td>b</td>
</tr>
<tr>
<td>1</td>
<td>c</td>
</tr>
<tr>
<td>1</td>
<td>d</td>
</tr>
<tr>
<td>1</td>
<td>e</td>
</tr>
<tr>
<td>1</td>
<td>f</td>
</tr>
<tr>
<td>1</td>
<td>g</td>
</tr>
<tr>
<td>1</td>
<td>h</td>
</tr>
<tr>
<td>1</td>
<td>i</td>
</tr>
<tr>
<td>1</td>
<td>j</td>
</tr>
<tr>
<td>1</td>
<td>k</td>
</tr>
<tr>
<td>1</td>
<td>l</td>
</tr>
<tr>
<td>1</td>
<td>m</td>
</tr>
<tr>
<td>1</td>
<td>n</td>
</tr>
<tr>
<td>1</td>
<td>o</td>
</tr>
<tr>
<td>1</td>
<td>p</td>
</tr>
<tr>
<td>1</td>
<td>q</td>
</tr>
<tr>
<td>1</td>
<td>r</td>
</tr>
<tr>
<td>1</td>
<td>s</td>
</tr>
<tr>
<td>1</td>
<td>t</td>
</tr>
<tr>
<td>1</td>
<td>u</td>
</tr>
<tr>
<td>1</td>
<td>v</td>
</tr>
</tbody>
</table>
</div>
</section>
</body>
</html>
const TIMEOUT = 10000; // Some book might take longer than this to renderer
describe("unforced-breaks-drop-break-after-then-break-inside", () => {
let page;
beforeAll(async () => {
page = await loadPage("breaks/unforced-breaks/long-table.html");
return page.rendered;
}, TIMEOUT);
afterAll(async () => {
if (!DEBUG) {
await page.close();
}
});
it("should render 2 pages", async () => {
let pages = await page.$$eval(".pagedjs_page", (r) => {
return r.length;
});
expect(pages).toEqual(2);
});
if (!DEBUG) {
it("should create a pdf", async () => {
let pdf = await page.pdf(PDF_SETTINGS);
expect(pdf).toMatchPDFSnapshot(1);
expect(pdf).toMatchPDFSnapshot(2);
});
}
});
...@@ -26,4 +26,4 @@ class BreakToken { ...@@ -26,4 +26,4 @@ class BreakToken {
} }
export default BreakToken; export default BreakToken;
\ No newline at end of file
...@@ -4,7 +4,7 @@ import EventEmitter from "event-emitter"; ...@@ -4,7 +4,7 @@ import EventEmitter from "event-emitter";
import Hook from "../utils/hook"; import Hook from "../utils/hook";
import Queue from "../utils/queue"; import Queue from "../utils/queue";
import {requestIdleCallback} from "../utils/utils"; import {requestIdleCallback} from "../utils/utils";
import {breakInsideAvoidNode, breakInsideAvoidParentNode} from "../utils/dom"; import { breakInsideAvoidNode, breakAfterAvoidNode, breakInsideAvoidParentNode, breakAfterAvoidParentNode, nextSignificantNode } from "../utils/dom";
const MAX_PAGES = false; const MAX_PAGES = false;
const MAX_LAYOUTS = false; const MAX_LAYOUTS = false;
...@@ -265,6 +265,31 @@ class Chunker { ...@@ -265,6 +265,31 @@ class Chunker {
} }
} }
async handleOverruledBreakAfter(node) {
const breakAfterNode = breakAfterAvoidNode(node);
if (breakAfterNode) {
const nextNode = nextSignificantNode(node);
if (nextNode) {
const page = this.addPage();
// QUESTION: should we emit the temporary page?
//this.emit("page", page);
const newBreakToken = await page.layout(node.parentNode, null, this.maxChars);
let overruled = false;
// indicates that content does not fit into the page
if (newBreakToken && typeof newBreakToken.node === "undefined") {
// we need to overrule this definition, remove "break-after: avoid" directive and reflow the previous page
delete breakAfterNode.dataset.breakAfter;
breakAfterNode.style = breakAfterNode.style + ";break-after: auto;";
delete nextNode.dataset.previousBreakAfter;
overruled = true;
}
// remove the temporary page
this.removePages(this.pages.length - 1);
return overruled;
}
}
}
async handleOverruledBreakInside(node) { async handleOverruledBreakInside(node) {
const breakInsideNode = breakInsideAvoidNode(node); const breakInsideNode = breakInsideAvoidNode(node);
if (breakInsideNode) { if (breakInsideNode) {
...@@ -272,7 +297,7 @@ class Chunker { ...@@ -272,7 +297,7 @@ class Chunker {
// QUESTION: should we emit the temporary page? // QUESTION: should we emit the temporary page?
//this.emit("page", page); //this.emit("page", page);
const newBreakToken = await page.layout(breakInsideNode, null, this.maxChars); const newBreakToken = await page.layout(breakInsideNode, null, this.maxChars);
const breakInsideAvoidParent = newBreakToken && breakInsideAvoidParentNode(newBreakToken.node); const breakInsideAvoidParent = newBreakToken && newBreakToken.node && breakInsideAvoidParentNode(newBreakToken.node);
// we are breaking inside a "break-inside: avoid" node which mean that we cannot comply with this rule! // we are breaking inside a "break-inside: avoid" node which mean that we cannot comply with this rule!
const overruleBreakInside = breakInsideAvoidParent && breakInsideAvoidParent.dataset && breakInsideNode.dataset && breakInsideNode.dataset.ref === breakInsideAvoidParent.dataset.ref; const overruleBreakInside = breakInsideAvoidParent && breakInsideAvoidParent.dataset && breakInsideNode.dataset && breakInsideNode.dataset.ref === breakInsideAvoidParent.dataset.ref;
if (overruleBreakInside) { if (overruleBreakInside) {
...@@ -347,8 +372,10 @@ class Chunker { ...@@ -347,8 +372,10 @@ class Chunker {
let reflowCurrentPage = false; let reflowCurrentPage = false;
if (breakToken && breakToken.node) { if (breakToken && breakToken.node) {
await this.handleBreaks(breakToken.node); await this.handleBreaks(breakToken.node);
const overruleBreakInside = await this.handleOverruledBreakInside(breakToken.node); if (await this.handleOverruledBreakAfter(breakToken.node)) {
if (overruleBreakInside) { reflowCurrentPage = typeof page !== "undefined";
breakToken = page && page.startToken;
} else if (await this.handleOverruledBreakInside(breakToken.node)) {
reflowCurrentPage = typeof page !== "undefined"; reflowCurrentPage = typeof page !== "undefined";
breakToken = page && page.startToken; breakToken = page && page.startToken;
} }
......
...@@ -67,13 +67,13 @@ class Layout { ...@@ -67,13 +67,13 @@ class Layout {
let next; let next;
let hasRenderedContent = false; let hasRenderedContent = false;
let newBreakToken; let newBreakToken = {node: undefined};
let length = 0; let length = 0;
let prevBreakToken = breakToken || new BreakToken(start); let prevBreakToken = breakToken || new BreakToken(start);
while (!done && !newBreakToken) { while (!done && typeof newBreakToken.node !== undefined) {
next = walker.next(); next = walker.next();
prevNode = node; prevNode = node;
node = next.value; node = next.value;
...@@ -90,8 +90,10 @@ class Layout { ...@@ -90,8 +90,10 @@ class Layout {
newBreakToken = this.findBreakToken(wrapper, source, bounds, prevBreakToken); newBreakToken = this.findBreakToken(wrapper, source, bounds, prevBreakToken);
if (newBreakToken && newBreakToken.equals(prevBreakToken)) { if (newBreakToken && newBreakToken.equals(prevBreakToken)) {
// TODO: we should probably return an objet (BreakToken?) with additional info
console.warn("Unable to layout item: ", prevNode); console.warn("Unable to layout item: ", prevNode);
return undefined; delete newBreakToken.node;
return newBreakToken;
} }
return newBreakToken; return newBreakToken;
} }
...@@ -115,7 +117,8 @@ class Layout { ...@@ -115,7 +117,8 @@ class Layout {
if (newBreakToken && newBreakToken.equals(prevBreakToken)) { if (newBreakToken && newBreakToken.equals(prevBreakToken)) {
console.warn("Unable to layout item: ", node); console.warn("Unable to layout item: ", node);
return undefined; delete newBreakToken.node;
return newBreakToken;
} }
length = 0; length = 0;
...@@ -169,7 +172,8 @@ class Layout { ...@@ -169,7 +172,8 @@ class Layout {
if (newBreakToken && newBreakToken.equals(prevBreakToken)) { if (newBreakToken && newBreakToken.equals(prevBreakToken)) {
console.warn("Unable to layout item: ", node); console.warn("Unable to layout item: ", node);
return undefined; delete newBreakToken.node;
return newBreakToken;
} }
if (newBreakToken) { if (newBreakToken) {
......
...@@ -610,6 +610,15 @@ export function breakInsideAvoidParentNode(node) { ...@@ -610,6 +610,15 @@ export function breakInsideAvoidParentNode(node) {
return null; return null;
} }
export function breakAfterAvoidParentNode(node) {
while ((node = node.parentNode)) {
if (node && node.dataset && node.dataset.breakAfter === "avoid") {
return node;
}
}
return null;
}
export function breakInsideAvoidNode(node) { export function breakInsideAvoidNode(node) {
if (node && node.dataset && node.dataset.breakInside === "avoid") { if (node && node.dataset && node.dataset.breakInside === "avoid") {
return node; return node;
...@@ -620,7 +629,23 @@ export function breakInsideAvoidNode(node) { ...@@ -620,7 +629,23 @@ export function breakInsideAvoidNode(node) {
if (child && child.dataset && child.dataset.breakInside === "avoid") { if (child && child.dataset && child.dataset.breakInside === "avoid") {
return child; return child;
} }
return breakInsideAvoidNode(child); breakInsideAvoidNode(child);
}
}
return null;
}
export function breakAfterAvoidNode(node) {
if (node && node.dataset && node.dataset.breakAfter === "avoid") {
return node;
}
let children = node.children;
if (children) {
for (const child of children) {
if (child && child.dataset && child.dataset.breakAfter === "avoid") {
return child;
}
breakAfterAvoidNode(child);
} }
} }
return null; return null;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment