Set the decision and its input
The synthetic page has eighty millimetres of usable height, a seventy-two millimetre spacer, and one twelve-millimetre business row with TOP and BOTTOM halves. Only eight millimetres remain. The whole-row requirement moves both halves to page two; ordinary fragmentation splits them across pages one and two.
| Case | Input JSON |
|---|---|
| Base test | {"keepWhole":true} |
Run the supplied comparison
The browser prints a real in-memory PDF and extraction identifies each marker’s page. The assertion is about this fixed layout and tested engine; it does not infer behaviour for every printer or a row taller than a page.
| Path | Output JSON |
|---|---|
| Faulty path | {"topPage":1,"bottomPage":2,"pageCount":2} |
| Required path | {"topPage":2,"bottomPage":2,"pageCount":2} |
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
import {pathToFileURL} from 'node:url';
const htmlEscape=s=>String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
async function withPage(action,options={}){
const require=createRequire(import.meta.url);const {chromium}=require('playwright');
const browser=await chromium.launch({headless:true});
try{const context=await browser.newContext(options);const page=await context.newPage();
const requests=[];await page.route('**/*',route=>{requests.push(route.request().url());return route.abort();});
return await action(page,context,requests);
}finally{await browser.close();}
}
async function pdfObservation(html){
const bytes=await withPage(async page=>{await page.setContent(html);return page.pdf({preferCSSPageSize:true,printBackground:true});});
const require=createRequire(import.meta.url);
const {getDocument}=await import(pathToFileURL(require.resolve('pdfjs-dist/legacy/build/pdf.mjs')).href);
const loadingTask=getDocument({data:Uint8Array.from(bytes),useSystemFonts:true,disableFontFace:true});
try{const document=await loadingTask.promise;const pages=[];for(let n=1;n<=document.numPages;n++){
const page=await document.getPage(n);const content=await page.getTextContent();
pages.push(content.items.filter(x=>typeof x.str==='string'&&x.str).map(x=>({text:x.str,x:x.transform[4],y:x.transform[5]})));
}return {pageCount:document.numPages,pages};}finally{await loadingTask.destroy();}
}
const printStyle='<style>@page{size:100mm 100mm;margin:10mm}body{margin:0;font:10px Arial}p{margin:0}</style>';
const run=async function run(i,corrected=true){
if(typeof i.keepWhole!=='boolean')return {held:true};
const style='<style>.space{height:72mm}.row{'+(corrected&&i.keepWhole?'break-inside:avoid;':'')+'}.part{height:6mm;line-height:6mm;orphans:1;widows:1}</style>';
const out=await pdfObservation(printStyle+style+'<div class="space"></div><div class="row"><p class="part">TOP</p><p class="part">BOTTOM</p></div>');
return {topPage:out.pages.findIndex(p=>p.some(x=>x.text==='TOP'))+1,bottomPage:out.pages.findIndex(p=>p.some(x=>x.text==='BOTTOM'))+1,pageCount:out.pageCount};
};
const cases=[{"input":{"keepWhole":true},"corrected":false,"expected":{"topPage":1,"bottomPage":2,"pageCount":2}},{"input":{"keepWhole":true},"corrected":true,"expected":{"topPage":2,"bottomPage":2,"pageCount":2}},{"input":{"keepWhole":false},"corrected":true,"expected":{"topPage":1,"bottomPage":2,"pageCount":2}},{"input":{"keepWhole":null},"corrected":true,"expected":{"held":true}}];
for(const test of cases){const actual=await run(test.input,test.corrected);assert.deepEqual(actual,test.expected);console.log(actual);}
Choose from the boundary evidence
If splitting is explicitly permitted, TOP on one and BOTTOM on two are acceptable. Unknown row policy is held before printing.
- Measure remaining space and row size
- Apply the whole-row condition
- Inspect the generated page positions
A taller-than-page row needs a separately supplied continuation policy.
| Condition | Input JSON | Expected output JSON |
|---|---|---|
| splitting explicitly permitted | {"keepWhole":false} | {"topPage":1,"bottomPage":2,"pageCount":2} |
| row requirement not supplied | {"keepWhole":null} | {"held":true} |
Where must both markers appear under the base requirement?
Show the worked answer
Page two together. The twelve-millimetre row cannot fit the remaining eight millimetres on page one.