Skip to content
Astera Technology

Count pages after adding the required print footer

Bind footer totals to the final generated layout.

Download complete case notes and answer keys ↓

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.

Filled local test worksheet
CaseInput JSON
Base test{"footerRequired":true}
Download editable CSV ↓

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.

Faulty and required outputs
PathOutput 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"]}
Download editable CSV ↓
Node24.14.1 programme; Playwright1.63 Chromium and PDF.js6.4.299 required
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
import {pathToFileURL} from 'node:url';
const htmlEscape=s=>String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
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);}
Download runnable example ↓

Choose from the boundary evidence

Without the footer-margin requirement the body remains two pages with no footers. An unknown footer requirement is held.

  1. Add the required footer space
  2. Generate the final layout
  3. Inspect every page total

Do not carry a pre-footer page total into the final document.

Editable boundary inputs and outputs
ConditionInput JSONExpected output JSON
body without footer margin{"footerRequired":false}{"pageCount":2,"footers":[null,null]}
footer requirement unknown{"footerRequired":null}{"held":true}
Download editable CSV ↓

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.

Example files

Related resources