Skip to content
Astera Technology

Check headings on printed continuation pages

Verify headings beyond the first page of a generated line list.

Download complete case notes and answer keys ↓

Set the decision and its input

The supplied print list has twelve fixed-height rows and an IDENTIFIER heading. The tested layout spans three pages. A header styled as an ordinary row appears only on page one; a repeating table header appears on pages one, two and three.

Filled local test worksheet
CaseInput JSON
Base test{"rows":12,"header":"IDENTIFIER"}
Download editable CSV ↓

Run the supplied comparison

The proof generates and extracts actual PDF pages, inspecting heading membership on every page. The demonstrated header feature does not establish pagination for an arbitrary stylesheet or unknown text metrics.

Faulty and required outputs
PathOutput JSON
Faulty path{"pageCount":3,"headerPages":[1]}
Required path{"pageCount":3,"headerPages":[1,2,3]}
Download editable CSV ↓
Node24.14.1 programme; Playwright1.63 Chromium and PDF.js6.4.299 required
import assert from 'node:assert/strict';
const smallWhole=(x,max)=>Number.isSafeInteger(x)&&x>=0&&x<=max;
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.header!=='string'||!i.header||i.header.length>40||!smallWhole(i.rows,20))return {held:true};
    const html=printStyle+'<style>table{border-collapse:collapse;width:100%}thead{display:'+(corrected?'table-header-group':'table-row-group')+'}th{padding:0;height:5mm;line-height:5mm}td{padding:0;height:15mm;line-height:15mm}</style><table><thead><tr><th>'+htmlEscape(i.header)+'</th></tr></thead><tbody>'+Array.from({length:i.rows},(_,n)=>'<tr><td>ROW'+(n+1)+'</td></tr>').join('')+'</tbody></table>';
    const out=await pdfObservation(html);return {pageCount:out.pageCount,headerPages:out.pages.map((p,n)=>p.some(x=>x.text===i.header)?n+1:null).filter(n=>n!==null)};
  };
const cases=[{"input":{"rows":12,"header":"IDENTIFIER"},"corrected":false,"expected":{"pageCount":3,"headerPages":[1]}},{"input":{"rows":12,"header":"IDENTIFIER"},"corrected":true,"expected":{"pageCount":3,"headerPages":[1,2,3]}},{"input":{"rows":3,"header":"IDENTIFIER"},"corrected":true,"expected":{"pageCount":1,"headerPages":[1]}},{"input":{"rows":12,"header":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

Three rows fit one page and need one heading. A required heading not supplied is held rather than invented by the renderer.

  1. Define required continuation headings
  2. Print the populated list
  3. Check each extracted page

An unknown heading cannot be guessed from the first data row.

Editable boundary inputs and outputs
ConditionInput JSONExpected output JSON
one-page list{"rows":3,"header":"IDENTIFIER"}{"pageCount":1,"headerPages":[1]}
required heading unknown{"rows":12,"header":null}{"held":true}
Download editable CSV ↓

Can a first-page heading screenshot prove the base list is complete?

Show the worked answer

No. Inspect pages two and three too. The corrected artefact has IDENTIFIER on all three pages.

Example files

Related resources