DavBfr / dart_pdf

Pdf creation module for dart/flutter
https://pub.dev/packages/pdf
Apache License 2.0
1.42k stars 632 forks source link

TooManyPagesException instead of spanning/overflowing the content of a cell in the next page #1589

Open theophile-br opened 9 months ago

theophile-br commented 9 months ago

Hi,

I don't know if it's a bug or if I don't use the API correctly. I would like to span a big content of a cell's table in the next page of my pdf.

This code below produce a TooManyPagesException, one of the cell in the table is too big to fit in one PDF A4 page. Do you have a workaround ?

import 'dart:io';

import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;

Future<void> main() async {
  final pdf = pw.Document();

  pdf.addPage(
    pw.MultiPage(
      pageFormat: PdfPageFormat.a4,
      build: (pw.Context context) => [
        pw.TableHelper.fromTextArray(
          headerCount: 0,
          cellStyle: const pw.TextStyle(
            fontSize: 8,
          ),
          cellAlignment: pw.Alignment.centerLeft,
          cellAlignments: {
            0: pw.Alignment.topRight,
            1: pw.Alignment.centerLeft,
          },
          columnWidths: {
            0: const pw.IntrinsicColumnWidth(flex: 0.3),
            1: const pw.IntrinsicColumnWidth(flex: 0.7)
          },
          data: [
            [
              pw.Text("This one"),
              pw.Text("is fine"),
            ],
            [
              pw.Text("This one isn't fine"),
              pw.Text("""because the content is too big for the page, it produce an TooManyPages error, 
                      I would like to span/overflow this cell's content to the next page
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                    """),
            ]
          ],
        ),
      ],
    ),
  );

  final file = File('example.pdf');
  await file.writeAsBytes(await pdf.save());
}

Thanks in advance for your support Best Regards

DavBfr commented 9 months ago

There is an overflow attribute to the Text, which can take span. Try that.

theophile-br commented 9 months ago

I already tried and unfortunately it doesn't work. The code below still produce the TooManyPagesException.

import 'dart:io';

import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;

Future<void> main() async {
  final pdf = pw.Document();

  pdf.addPage(
    pw.MultiPage(
      pageFormat: PdfPageFormat.a4,
      build: (pw.Context context) => [
        pw.TableHelper.fromTextArray(
          headerCount: 0,
          cellStyle: const pw.TextStyle(
            fontSize: 8,
          ),
          cellAlignment: pw.Alignment.centerLeft,
          cellAlignments: {
            0: pw.Alignment.topRight,
            1: pw.Alignment.centerLeft,
          },
          columnWidths: {
            0: const pw.IntrinsicColumnWidth(flex: 0.3),
            1: const pw.IntrinsicColumnWidth(flex: 0.7)
          },
          data: [
            [
              pw.Text("This one", overflow: pw.TextOverflow.span),
              pw.Text("is fine", overflow: pw.TextOverflow.span),
            ],
            [
              pw.Text("This one isn't fine", overflow: pw.TextOverflow.span),
              pw.Text(
                  """because the content is too big for the page, it produce an TooManyPages error, 
                      I would like to span the content of this cells to the next page
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                    """,
                  overflow: pw.TextOverflow.span),
            ]
          ],
        ),
      ],
    ),
  );

  final file = File('example.pdf');
  await file.writeAsBytes(await pdf.save());
}
theophile-br commented 9 months ago

This error happens because the Text is not on top level and the PDF don't know how to handle a cell bigger than the PDF page. The same thing happens with Column. See example below

import 'dart:io';

import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;

Future<void> main() async {
  final pdf = pw.Document();

  pdf.addPage(
    pw.MultiPage(
      pageFormat: PdfPageFormat.a4,
      build: (pw.Context context) => [
        pw.Column(
          children: [
            pw.Text("This one is fine", overflow: pw.TextOverflow.span),
            pw.Text(
                """This one isn't fine because the content is too big for the page, it produce an TooManyPages error, 
                      I would like to span the content of this cells to the next page
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                    """,
                overflow: pw.TextOverflow.span),
          ],
        ),
      ],
    ),
  );

  final file = File('example.pdf');
  await file.writeAsBytes(await pdf.save());
}
waleedbinnaeem7 commented 9 months ago

to fix Try to avoid using widgets that do not support pagination, such as Wrap or Column, and use widgets that can span across multiple pages, such as ListView or Table.this working for me .

theophile-br commented 9 months ago

Replacing Column with ListView (like the example below) still doesn't work for me.

import 'dart:io';

import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;

Future<void> main() async {
  final pdf = pw.Document();

  pdf.addPage(
    pw.MultiPage(
      pageFormat: PdfPageFormat.a4,
      build: (pw.Context context) => [
        pw.ListView(
          children: [
            pw.Text("This one is fine", overflow: pw.TextOverflow.span),
            pw.Text(
                """This one isn't fine because the content is too big for the page, it produce an TooManyPages error, 
                      I would like to span the content of this cells to the next page
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                    """,
                overflow: pw.TextOverflow.span),
          ],
        ),
      ],
    ),
  );

  final file = File('example.pdf');
  await file.writeAsBytes(await pdf.save());
}
djol commented 9 months ago

I can confirm that the same is occurring for me [pdf 3.10.7, Flutter 3.19.0] having previously worked fine. I have not been able to identify which version caused the problem.

In my use, I use MultiPage to build a PDF containing a table of text. Optionally, images are added to the PDF if they are attached to the database record that builds the table. Previously an image either appeared embedded on the first PDF page (below the table) if there was room, or paginated to the next page where it was scaled down to fit as required. Perfect behaviour!

Now I also get the dreaded TooManyPagesException error if an image is added that is too big to fit in the remaining space of the first page of the PDF.

As a temporary workaround I now build the PDF with any attachment images added as new (multi)pages, doc.addPage(pw.MultiPage(...)) with images in a sole container on the page, max height 720.

This was my original code that once worked but now gives the error:

    doc.addPage(
      pw.MultiPage(
        pageFormat: PdfPageFormat.a4.copyWith(marginBottom: 0.7 * PdfPageFormat.cm, marginTop: 0.7 * PdfPageFormat.cm),
        crossAxisAlignment: pw.CrossAxisAlignment.start,
        header: ...,
        footer: ...,
        build: (pw.Context context) => <pw.Widget>[
          pw.ListView(children: [

            ... TABLE CODE ...

            // add attachments if present
            if (attachmentImages.isNotEmpty)
              pw.ListView(children: [
                for (PdfImage attachmentImage in attachmentImages)
                  pw.Container(
                      alignment: pw.Alignment.center,
                      padding: const pw.EdgeInsets.only(left: 10, right: 10),
                      child: pw.Image(
                        pw.ImageProxy(attachmentImage),
                        // fit: pw.BoxFit.fitHeight  <-- I tried many different values for fit 
                      )),
              ])
         ]) ...

My updated code moves the image attachment loop outside the original MultiPage widget to its own widget, thus forcing (non-ideally) each image onto its own page where I can control the height of the container. Anything more than a height of 720 (the available height after allowing for margins & header/footer) raises TooManyPagesException. I have yet to find a way to dynamically include the image (when it fits) on the first page below the table data, or to fit multiple smaller images together on single pages if they are small enough to fit.

    // add attachments if present
    // TEMP FIX for MultiPage bug giving TooManyPagesException
    if (attachmentImages.isNotEmpty)
      for (PdfImage attachmentImage in attachmentImages)
        doc.addPage(pw.MultiPage(
            pageFormat:
                PdfPageFormat.a4.copyWith(marginBottom: 0.7 * PdfPageFormat.cm, marginTop: 0.7 * PdfPageFormat.cm),
            crossAxisAlignment: pw.CrossAxisAlignment.start,
            header: (pw.Context context) => pageHeader(context, event),
            footer: (pw.Context context) => pageFooter(context, user),
            build: (pw.Context context) {
              return [
                pw.Container(
                    alignment: pw.Alignment.center,
                    padding: const pw.EdgeInsets.all(10),
                    constraints: pw.BoxConstraints(maxHeight: 720),
                    child: pw.Image(
                      pw.ImageProxy(attachmentImage),
                    ))
              ];
            }));
  }
theophile-br commented 8 months ago

Unfortunately I can't find any workaround for now.

Putting all my widget on the first level solve this issue (see code below)), but this is not what I need. I need to be able to build layout, table, row and column nested

import 'dart:io';

import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;

Future<void> main() async {
  final pdf = pw.Document();

  pdf.addPage(
    pw.MultiPage(
      pageFormat: PdfPageFormat.a4,
      build: (pw.Context context) => [
            pw.Text("This one is fine", overflow: pw.TextOverflow.span),
            pw.Text(
                """This one isn't fine because the content is too big for the page, it produce an TooManyPages error, 
                      I would like to span the content of this cells to the next page
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                      A
                    """,
                overflow: pw.TextOverflow.span),
      ],
    ),
  );

  final file = File('example.pdf');
  await file.writeAsBytes(await pdf.save());
}
mminhlequang commented 6 months ago

up

olivierDelierre commented 5 months ago

Up

theophile-br commented 4 months ago

Do you have any update on this ?

A comment needs clarification in the file multi_page.dart line 317 "What to do if the widget is too big for the page?"

      // What to do if the widget is too big for the page?
      if (offsetStart! - child.box!.height < offsetEnd) {
        // If it is not a multi-page widget and its height
        // is smaller than a full new page, we schedule a new page creation
        if (child.box!.height <= pageHeight - pageHeightMargin && !canSpan) {
          context = null;
          continue;
        }
ozz-rjq commented 4 months ago

any update?

nurullahturkoglu commented 1 week ago

up