Set the decision and its input
The fixed body fills two pages with eighty millimetres of usable page height. Reserving the required footer margin reduces that height to seventy, so the final PDF spans three. Reusing the earlier total two produces Page 3 of 2. The corrected footer uses the final page count.
| Case | Input JSON |
|---|---|
| Base test | {"footerRequired":true} |
Run the supplied comparison
The browser generates the final PDF and text extraction checks every footer. No estimated body count is substituted for the post-layout result. CSS page counters are exercised in the tested engine, not promised across all renderers.
| Path | Output JSON |
|---|---|
| Faulty path | {"pageCount":3,"footers":["Page 1 of 2","Page 2 of 2","Page 3 of 2"]} |
| Required path | {"pageCount":3,"footers":["Page 1 of 3","Page 2 of 3","Page 3 of 3"]} |
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.footerRequired!=='boolean')return {held:true};
const footer=i.footerRequired?'@page{margin-bottom:20mm;@bottom-center{content:"Page " counter(page) " of " '+(corrected?'counter(pages)':'"2"')+';font:10px Arial}}':'';
const html=printStyle+'<style>'+footer+'.body{height:160mm}.line{height:10mm;line-height:10mm}</style><div class="body">'+Array.from({length:16},(_,n)=>'<p class="line">LINE'+(n+1)+'</p>').join('')+'</div>';
const out=await pdfObservation(html);return {pageCount:out.pageCount,footers:out.pages.map(p=>p.map(x=>x.text).find(s=>s.startsWith('Page '))??null)};
};
const cases=[{"input":{"footerRequired":true},"corrected":false,"expected":{"pageCount":3,"footers":["Page 1 of 2","Page 2 of 2","Page 3 of 2"]}},{"input":{"footerRequired":true},"corrected":true,"expected":{"pageCount":3,"footers":["Page 1 of 3","Page 2 of 3","Page 3 of 3"]}},{"input":{"footerRequired":false},"corrected":true,"expected":{"pageCount":2,"footers":[null,null]}},{"input":{"footerRequired":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
Without the footer-margin requirement the body remains two pages with no footers. An unknown footer requirement is held.
- Add the required footer space
- Generate the final layout
- Inspect every page total
Do not carry a pre-footer page total into the final document.
| Condition | Input JSON | Expected output JSON |
|---|---|---|
| body without footer margin | {"footerRequired":false} | {"pageCount":2,"footers":[null,null]} |
| footer requirement unknown | {"footerRequired":null} | {"held":true} |
What should the three final footers say?
Show the worked answer
Page 1 of 3, Page 2 of 3 and Page 3 of 3. All belong to the same final three-page artefact.