Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[syncfusion_flutter_pdf] Issue with Exporting Tall Widgets to PDF Using Syncfusion in Flutter #2170

Open
azazadev opened this issue Nov 18, 2024 · 1 comment
Labels
pdf PDF component waiting for customer response Cannot make further progress until the customer responds.

Comments

@azazadev
Copy link

azazadev commented Nov 18, 2024

Bug description

In our Flutter-based dashboarding app, users can view and interact with charts (gauge, bar, line, data grid, etc.) and custom cards that may contain text, trends, or other components. Users can select these charts and export them to a PDF using Syncfusion's PDF functionality.

The issue arises when exporting charts or cards taller than the screen height (e.g., more than 100% of the viewport height). The exported PDF only captures the visible portion of the widget, while the rest is truncated, leading to incomplete exports for tall widgets

Steps to reproduce

  1. Create a widget (e.g., a chart or card) that exceeds the screen height (e.g., 200% of the viewport).
  2. Add the widget to the app with the ability to scroll to view the full content.
  3. Use Syncfusion's PDF functionality to export the widget to a PDF.
    Check the exported PDF.

Expected Behavior:
The entire widget, including all scrollable or tall content, should be fully included in the exported PDF.

Actual Behavior:
Only the visible portion of the widget (within the screen bounds) is exported, and the rest is truncated.

Code sample

  Future<void> exportSelectedCharts(BuildContext context) async {
    try {
      PdfDocument document = PdfDocument();

      // Add Title and Body to the PDF
      PdfPage titlePage = document.pages.add();
      titlePage.graphics.drawString(
        'MobileApp - Shared Charts',
        PdfStandardFont(PdfFontFamily.helvetica, 24),
        bounds: Rect.fromLTWH(0, 0, titlePage.getClientSize().width, 50),
      );
      titlePage.graphics.drawString(
        'Explore the charts ...',
        PdfStandardFont(PdfFontFamily.helvetica, 16),
        bounds: Rect.fromLTWH(0, 60, titlePage.getClientSize().width, 30),
      );

      bool isFirstChart = true;

      for (var identifier in _selectedCharts) {
        final card = getFavoriteCards()[identifier];
        if (card != null) {
          GlobalKey boundaryKey = GlobalKey();
          final imageBytes = await _captureWidgetAsImage(
            context,
            boundaryKey,
            Padding(
              padding: const EdgeInsets.only(top: 80, bottom: 20),
              child: MyCustomCard(
                // this is my card that can contain any syncfusion chart and height can be dynamic
                key: UniqueKey(),
              ),
            ),
            pixelRatio: 3.0,
          );

          if (imageBytes != null) {
            PdfPage page;

            if (isFirstChart) {
              // Use the existing title page for the first chart
              page = titlePage;
              isFirstChart = false;
            } else {
              // Add a new page for each subsequent chart
              page = document.pages.add();
            }

            final PdfBitmap bitmap = PdfBitmap(imageBytes);

            // Scale the image to its original pixel size, and center it on the page
            double originalImageWidth = bitmap.width.toDouble();
            double originalImageHeight = bitmap.height.toDouble();

            // Check if the image fits within the page, otherwise scale down to fit
            double pageWidth = page.getClientSize().width;
            double pageHeight = page.getClientSize().height;

            double scale = 1.0;
            if (originalImageWidth > pageWidth || originalImageHeight > pageHeight) {
              double widthScale = pageWidth / originalImageWidth;
              double heightScale = pageHeight / originalImageHeight;
              scale = widthScale < heightScale ? widthScale : heightScale;
            }

            double scaledWidth = originalImageWidth * scale;
            double scaledHeight = originalImageHeight * scale;

            double x = (pageWidth - scaledWidth) / 2;
            double y = (pageHeight - scaledHeight) / 2;

            page.graphics.drawImage(
              bitmap,
              Rect.fromLTWH(x, y, scaledWidth, scaledHeight),
            );
          } else {
            debugPrint('Error capturing image for $identifier: imageBytes is null');
          }
        }
      }

      final directory = await getApplicationDocumentsDirectory();
      final path = '${directory.path}/selected_charts.pdf';
      File file = File(path);
      await file.writeAsBytes(await document.save());
      document.dispose();
      final xFile = XFile(path);

      await Share.shareXFiles(
        [xFile],
        text: 'MobileApp',
      ).whenComplete(() {
        _selectedCharts.clear();
        _selectionNotifiers.clear();
        _isSelectionMode = false;
        notifyListeners();
      });
    } catch (e) {
      debugPrint('Error during export: $e');
    }
  }

  Future<Uint8List?> _captureWidgetAsImage(
    BuildContext context,
    GlobalKey key,
    Widget widget, {
    double pixelRatio = 1.0,
  }) async {
    Completer<Uint8List?> completer = Completer();
    OverlayEntry overlayEntry = OverlayEntry(
      builder: (context) => MaterialApp(
        home: MediaQuery(
          data: MediaQueryData.fromView(WidgetsBinding.instance.window),
          child: Scaffold(
            body: RepaintBoundary(
              key: key,
              child: Container(
                padding: const EdgeInsets.all(16),
                child: widget,
              ),
            ),
          ),
        ),
      ),
    );

    Overlay.of(context).insert(overlayEntry);
    await _ensureWidgetIsPainted();

    final boundary = key.currentContext?.findRenderObject() as RenderRepaintBoundary?;
    if (boundary != null) {
      final image = await boundary.toImage(pixelRatio: pixelRatio);
      final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
      completer.complete(byteData?.buffer.asUint8List());
    } else {
      completer.complete(null);
    }

    overlayEntry.remove();
    return completer.future;
  }

  Future<void> _ensureWidgetIsPainted() async {
    await Future.delayed(const Duration(milliseconds: 500));
  }

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Stack Traces

Stack Traces
[Add the Stack Traces here]

On which target platforms have you observed this bug?

Android, iOS

Flutter Doctor output

• Flutter version 3.24.3 on channel stable at /Users/...
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 2663184aa7 (10 weeks ago), 2024-09-11 16:27:48 -0500
• Engine revision 36335019a8
• Dart version 3.5.3
• DevTools version 2.37.3
@VijayakumarMariappan VijayakumarMariappan added pdf PDF component open Open labels Nov 21, 2024
@irfanajaffer
Copy link

Hi ,

We have attempted to reproduce the reported issue on our end, but the data is being exported to PDF as expected. For your reference, we have prepared a sample and attached it. Kindly try running this sample on your end and let us know if you require any further assistance.

Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/export_chart_sample891922016

For more details, you can also refer to the following documentation:
Export Cartesian Chart to PDF

If the issue persists, we kindly request you to modify the sample and share it with us. This will help us analyze the problem more effectively and provide you with a prompt solution.

Regards,

Irfana J.

@chinnumuniyappan chinnumuniyappan added waiting for customer response Cannot make further progress until the customer responds. and removed open Open labels Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pdf PDF component waiting for customer response Cannot make further progress until the customer responds.
Projects
None yet
Development

No branches or pull requests

4 participants