Create Multi-Page PDF Tables from MySQL using PHP FPDF

When a database contains more records than one PDF page can hold, the report must continue onto additional pages. With FPDF we can manage this in two different ways.

Database records
       |
       v
+-----------------------------+
| Strategy 1                  |
| Fixed records per page      |
+-----------------------------+
       |
       OR
       |
+-----------------------------+
| Strategy 2                  |
| Check available PDF space   |
+-----------------------------+
       |
       v
Multi-page PDF table

The first method is useful when every row has a predictable height. The second method is more flexible because it checks the current vertical position before adding the next record.

Two Ways to Create Multi-Page PDF Tables Top ↑

Both approaches produce multiple PDF pages, but they decide when to create a new page differently.

Fixed records per page
Calculate the total pages first and fetch a fixed number of rows for each page.
Available-space method
Read the current Y coordinate and add another page only when the next row will not fit.

Part I: Fixed Number of Records per PDF Page Top ↑

In this method we decide how many database records should appear on each page. The example uses 10 records per page.

  1. Count all records in the student table.
  2. Set the number of records allowed on each PDF page.
  3. Calculate the required number of pages with ceil().
  4. Add one FPDF page for each group of records.
  5. Fetch only the rows required for that page.
  6. Repeat the table header on every page.

Count the Total Number of Database Records Top ↑

First count the records that will be included in the report.

$sql_total="SELECT COUNT(*) FROM student";
$total_records=(int)$dbo->query($sql_total)->fetchColumn();

fetchColumn() is enough here because the COUNT query returns one value.

Calculate the Number of PDF Pages Top ↑

Set the records per page and divide the total record count by that value.

$records_per_page=10;

$total_pages=(int)ceil(
    $total_records/$records_per_page
);

For example:

35 records / 10 per page = 4 PDF pages

The final page contains the remaining records.

Fetch Records using LIMIT and OFFSET Top ↑

The page number determines the SQL offset.

$offset=$page*$records_per_page;

Instead of inserting those values directly into the SQL string, bind them as integers:

$sql="SELECT id,name,class,mark,gender
      FROM student
      ORDER BY id
      LIMIT :limit OFFSET :offset";

$stmt=$dbo->prepare($sql);
$stmt->bindValue(':limit',$records_per_page,PDO::PARAM_INT);
$stmt->bindValue(':offset',$offset,PDO::PARAM_INT);
$stmt->execute();

$rows=$stmt->fetchAll(PDO::FETCH_ASSOC);

The explicit ORDER BY id is important because pagination should use a predictable record order.

Complete PHP Code: Fixed Records per PDF Page Top ↑

<?php
require 'config.php';
require 'fpdf.php';

function addTableHeader($pdf,$width_cell)
{
    $pdf->SetFont('Arial','B',14);
    $pdf->SetFillColor(193,229,252);

    $pdf->Cell(
        $width_cell[0],
        10,
        'ID',
        1,
        0,
        'C',
        true
    );

    $pdf->Cell(
        $width_cell[1],
        10,
        'NAME',
        1,
        0,
        'C',
        true
    );

    $pdf->Cell(
        $width_cell[2],
        10,
        'CLASS',
        1,
        0,
        'C',
        true
    );

    $pdf->Cell(
        $width_cell[3],
        10,
        'MARK',
        1,
        0,
        'C',
        true
    );

    $pdf->Cell(
        $width_cell[4],
        10,
        'GENDER',
        1,
        1,
        'C',
        true
    );
}

try{
    $sql_total="SELECT COUNT(*) FROM student";
    $total_records=(int)$dbo->query($sql_total)->fetchColumn();

    $records_per_page=10;

    $total_pages=$total_records>0
        ? (int)ceil($total_records/$records_per_page)
        : 1;

    $pdf=new FPDF();
    $pdf->SetAutoPageBreak(true,15);

    $width_cell=[20,50,40,40,40];

    $sql="SELECT id,name,class,mark,gender
          FROM student
          ORDER BY id
          LIMIT :limit OFFSET :offset";

    $stmt=$dbo->prepare($sql);

    for($page=0;$page<$total_pages;$page++){
        $pdf->AddPage();
        addTableHeader($pdf,$width_cell);

        $offset=$page*$records_per_page;

        $stmt->bindValue(
            ':limit',
            $records_per_page,
            PDO::PARAM_INT
        );

        $stmt->bindValue(
            ':offset',
            $offset,
            PDO::PARAM_INT
        );

        $stmt->execute();
        $rows=$stmt->fetchAll(PDO::FETCH_ASSOC);

        $pdf->SetFont('Arial','',12);
        $pdf->SetFillColor(235,236,236);

        $fill=false;

        foreach($rows as $row){
            $pdf->Cell(
                $width_cell[0],
                10,
                (string)$row['id'],
                1,
                0,
                'C',
                $fill
            );

            $pdf->Cell(
                $width_cell[1],
                10,
                (string)$row['name'],
                1,
                0,
                'L',
                $fill
            );

            $pdf->Cell(
                $width_cell[2],
                10,
                (string)$row['class'],
                1,
                0,
                'C',
                $fill
            );

            $pdf->Cell(
                $width_cell[3],
                10,
                (string)$row['mark'],
                1,
                0,
                'C',
                $fill
            );

            $pdf->Cell(
                $width_cell[4],
                10,
                (string)$row['gender'],
                1,
                1,
                'C',
                $fill
            );

            $fill=!$fill;
        }

        if(!$rows && $page===0){
            $pdf->SetFont('Arial','',12);

            $pdf->Cell(
                190,
                10,
                'No student records found.',
                1,
                1,
                'C'
            );
        }
    }

    $pdf->Output(
        'I',
        'student-multiple-pages.pdf'
    );
}catch(PDOException $e){
    error_log($e->getMessage());
    exit('Unable to generate the PDF report.');
}
DEMO: Fixed Number of Records per PDF Page

Part II: Add PDF Pages based on Available Space Top ↑

The fixed-record method works well when every database row uses the same height. A more flexible approach is to check whether enough vertical space remains before drawing the next row.

Current Y position
       +
Next row height
       |
       v
Will row fit?
   /       \
 Yes        No
  |          |
Draw row   AddPage()
             |
             v
        Repeat header

This avoids having to decide in advance how many records must fit on each page.

Check the Remaining Space on the PDF Page Top ↑

The original example used a hard-coded value of 280. A more adaptable approach uses the current page height.

$row_height=10;
$bottom_margin=15;
$page_height=$pdf->GetPageHeight();

if(
    $pdf->GetY()+$row_height
    >
    $page_height-$bottom_margin
){
    $pdf->AddPage();
}

This is better than assuming one specific page height because the condition is based on the actual PDF page dimensions.

When a new page is created, the table headings should appear again before the next record.

if(
    $pdf->GetY()+$row_height
    >
    $page_height-$bottom_margin
){
    $pdf->AddPage();
    addTableHeader($pdf,$width_cell);
}

Using one helper function keeps the header formatting identical on every page.

Complete PHP Code: Dynamic Page Breaks based on Available Space Top ↑

<?php
require 'config.php';
require 'fpdf.php';

function addTableHeader($pdf,$width_cell)
{
    $pdf->SetFont('Arial','B',14);
    $pdf->SetFillColor(193,229,252);

    $headers=['ID','NAME','CLASS','MARK','GENDER'];

    foreach($headers as $index=>$header){
        $line=$index===count($headers)-1
            ? 1
            : 0;

        $pdf->Cell(
            $width_cell[$index],
            10,
            $header,
            1,
            $line,
            'C',
            true
        );
    }
}

try{
    $sql="SELECT id,name,class,mark,gender
          FROM student
          ORDER BY id";

    $stmt=$dbo->prepare($sql);
    $stmt->execute();

    $pdf=new FPDF();
    $pdf->SetAutoPageBreak(false);
    $pdf->AddPage();

    $width_cell=[20,50,40,40,40];
    $row_height=10;
    $bottom_margin=15;
    $page_height=$pdf->GetPageHeight();

    addTableHeader($pdf,$width_cell);

    $pdf->SetFont('Arial','',12);
    $pdf->SetFillColor(235,236,236);

    $fill=false;
    $has_rows=false;

    while($row=$stmt->fetch(PDO::FETCH_ASSOC)){
        $has_rows=true;

        if(
            $pdf->GetY()+$row_height
            >
            $page_height-$bottom_margin
        ){
            $pdf->AddPage();
            addTableHeader($pdf,$width_cell);

            $pdf->SetFont(
                'Arial',
                '',
                12
            );

            $pdf->SetFillColor(
                235,
                236,
                236
            );
        }

        $pdf->Cell(
            $width_cell[0],
            $row_height,
            (string)$row['id'],
            1,
            0,
            'C',
            $fill
        );

        $pdf->Cell(
            $width_cell[1],
            $row_height,
            (string)$row['name'],
            1,
            0,
            'L',
            $fill
        );

        $pdf->Cell(
            $width_cell[2],
            $row_height,
            (string)$row['class'],
            1,
            0,
            'C',
            $fill
        );

        $pdf->Cell(
            $width_cell[3],
            $row_height,
            (string)$row['mark'],
            1,
            0,
            'C',
            $fill
        );

        $pdf->Cell(
            $width_cell[4],
            $row_height,
            (string)$row['gender'],
            1,
            1,
            'C',
            $fill
        );

        $fill=!$fill;
    }

    if(!$has_rows){
        $pdf->SetFont('Arial','',12);

        $pdf->Cell(
            190,
            10,
            'No student records found.',
            1,
            1,
            'C'
        );
    }

    $pdf->Output(
        'I',
        'student-dynamic-pages.pdf'
    );
}catch(PDOException $e){
    error_log($e->getMessage());
    exit('Unable to generate the PDF report.');
}
DEMO: Multi-Page PDF based on Available Space

Which Multi-page Method Should You Use? Top ↑

Use fixed records per page when Top ↑

  • every row has the same height,
  • you want an exact number of records on each page,
  • you want to calculate the total number of PDF pages before generating them.

Use available-space checking when Top ↑

  • the amount of content above the table can change,
  • different page sizes may be used,
  • you want page creation to depend on the actual PDF cursor position.
For the fixed-height Cell() rows used in this tutorial, both methods work well. If table rows later contain wrapped MultiCell() content, the required row height must also be calculated before deciding whether the row fits on the current page.

Video: Generate Multiple PDF Pages Dynamically Top ↑

Generate PDF with Multiple Pages Dynamically based on Number of Database Records using PHP FPDF

Download the Database-to-PDF Project Top ↑

The main database PDF tutorial contains the downloadable project with the sample student table, MySQL version and SQLite-supported version.

Download Database-to-PDF Project

Common Problems with Multi-Page PDF Tables Top ↑

Records appear in an inconsistent order Top ↑

Use an explicit ORDER BY clause when paginating database records. Without a defined order, rows should not be assumed to appear in the same sequence every time.

SQL LIMIT or OFFSET causes an error Top ↑

Bind both values as PDO::PARAM_INT. They are numeric SQL values and should not be treated as quoted strings.

Table headings appear only on the first page Top ↑

Call the table-header function immediately after every AddPage().

The last row is too close to the bottom edge Top ↑

Reserve a bottom margin and check the current Y coordinate plus the next row height before drawing the row.

The dynamic example adds a page too early or too late Top ↑

Make sure $row_height matches the actual height used by the row Cells, and adjust the bottom margin when the document has a footer or additional page content.

Wrapped text makes rows overlap Top ↑

A fixed-height Cell() does not wrap text. If a table uses MultiCell(), calculate the full rendered row height before deciding whether the next row fits on the page.

Database Records to PDF Table Single Student Mark Sheet

FPDF Table Cell() MultiCell() Add Images

Frequently Asked Questions Top ↑

Q1: How do I create multiple PDF pages from MySQL records using PHP FPDF?

Fetch the database records and call AddPage() whenever a new PDF page is required. Repeat the table header after each page is added.

Q2: How can I display a fixed number of records on each PDF page?

Count the total records, set a records-per-page value, calculate the required pages with ceil(), and use LIMIT and OFFSET to fetch the records for each page.

Q3: How do I add a new PDF page only when the current page is full?

Read the current Y position with GetY(), add the next row height and compare the result with the page height minus the reserved bottom margin.

Q4: How do I repeat the table headings on every FPDF page?

Place the heading Cells inside a reusable function and call that function immediately after every AddPage().

Q5: Why should LIMIT and OFFSET be bound as integers in PDO?

LIMIT and OFFSET expect numeric values. Binding them with PDO::PARAM_INT sends the values with the appropriate type.

Q6: Which method is better: fixed records per page or available-space checking?

Fixed pagination is convenient when every row has a known height. Available-space checking is more flexible when the amount of content or page layout can vary.

Q7: Can the same page-break technique be used with MultiCell rows?

Yes, but the complete rendered row height must be calculated before deciding whether the row fits because MultiCell can wrap text over several lines.


Database PDF Table Student Mark Sheet


Subscribe to our YouTube Channel here



plus2net.com











PHP video Tutorials
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
©2000-2026   plus2net.com   All rights reserved worldwide Privacy Policy Disclaimer