DavBfr / dart_pdf

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

with the RichText widget the text is not breaking the line and adjusting with the textAlign justify #1706

Open insinfo opened 1 month ago

insinfo commented 1 month ago

with the RichText widget the text is not breaking the line and adjusting with the textAlign justify

pdf: ^3.11.0

import 'dart:io';

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

void main() async {
  var nomeCandidato = 'Ana Lívia dos Santos Vieira';
  var nomeCurso = 'Curso Avançado de Excel';
  final now = DateTime.now();

  final pdf = pw.Document();
  final bg = pw.Image(await networkImage(
      'https://brillium-www-public.s3.amazonaws.com/Support-Resources/blank-certs/Brillium%20A4%20Certificate-Blue-Blank.png'));
  final svgImage = pw.SvgImage(
      svg: await networkSVG(
          'https://s3-eu-west-1.amazonaws.com/worldvectorlogologos/logos/aws-quali.svg?response-content-type=application%2Foctet-stream&response-content-disposition=attachment%3B%20filename%3D%22aws-quali.svg%22&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIABU4EDO27KZDOIA%2F20240731%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20240731T195546Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Signature=892b0d39e658818942d9b2dba6ac1336ba762ad29b92ea5b9cf7350e1432d6d2'),
      width: 80);

  final bodyTextStyle = pw.TextStyle(
    fontSize: 16,
    // color: pw.PdfColor.fromHex('#f00'),
    fontWeight: pw.FontWeight.normal,
  );
  final strongTextStyle = pw.TextStyle(
    fontSize: 16,
    //color: pw.PdfColor.fromHex('#f00'),
    fontWeight: pw.FontWeight.bold,
  );

  final assNomeTextStyle = pw.TextStyle(
    fontSize: 15,
    // color: pw.PdfColor.fromHex('#f00'),
    fontWeight: pw.FontWeight.bold,
  );
  final assCargoTextStyle = pw.TextStyle(
    fontSize: 14,
    //color: pw.PdfColor.fromHex('#f00'),
    fontWeight: pw.FontWeight.normal,
  );

  pdf.addPage(pw.Page(
      pageFormat: pw.PdfPageFormat.a4,
      orientation: pw.PageOrientation.landscape,
      margin: pw.EdgeInsets.all(0),
      build: (pw.Context context) {
        return pw.Stack(children: [
          bg,
          pw.Container(
              child: pw.Column(
            mainAxisAlignment: pw.MainAxisAlignment.start,
            crossAxisAlignment: pw.CrossAxisAlignment.start,
            children: [
              pw.Row(mainAxisAlignment: pw.MainAxisAlignment.end, children: [
                pw.Padding(
                  padding: pw.EdgeInsets.fromLTRB(0, 80, 15, 0),
                  child: pw.SizedBox(height: 120, child: svgImage),
                ),
              ]),
              pw.SizedBox(height: 60),
              //texto principal
              pw.Row(
                  mainAxisAlignment: pw.MainAxisAlignment.start,
                  crossAxisAlignment: pw.CrossAxisAlignment.start,
                  children: [
                    pw.Container(
                      width: double.infinity,
                      padding: pw.EdgeInsets.fromLTRB(150, 0, 0, 0),
                      child: pw.RichText(
                          textDirection: pw.TextDirection.ltr,
                          textAlign: pw.TextAlign.justify,
                          softWrap: true,
                          maxLines: 10,
                          text: pw.TextSpan(
                              style: bodyTextStyle,
                              text: 'Certificamos que ',
                              children: [
                                pw.TextSpan(
                                  style: strongTextStyle,
                                  text: nomeCandidato.toUpperCase(),
                                ),
                                pw.TextSpan(
                                  style: bodyTextStyle,
                                  text: ' concluiu com aproveitamento o curso ',
                                ),
                                pw.TextSpan(
                                  style: strongTextStyle,
                                  text: nomeCurso.toUpperCase(),
                                ),
                                pw.TextSpan(
                                  style: bodyTextStyle,
                                  text: ' do Programa Municipal de Qualificação Profissional de Rio das Ostras, ' +
                                      'ministrado pela Secretaria de Gestão Pública, perfazendo um total de ',
                                ),
                                pw.TextSpan(
                                  style: strongTextStyle,
                                  text: '102 horas/aula.',
                                ),
                              ])),
                    ),
                  ]),
              //Data
              pw.Row(
                  mainAxisAlignment: pw.MainAxisAlignment.end,
                  crossAxisAlignment: pw.CrossAxisAlignment.start,
                  children: [
                    pw.Container(
                      padding: pw.EdgeInsets.fromLTRB(0, 50, 160, 0),
                      child: pw.Text(
                          '''Rio das Ostras, ${DateFormat("dd 'de' MMMM 'de' yyyy").format(now)}''',
                          style: bodyTextStyle),
                    )
                  ]),
              //Assinaturas
              pw.Padding(
                padding: pw.EdgeInsets.fromLTRB(150, 100, 0, 0),
                child: pw.Flex(
                    direction: pw.Axis.horizontal,
                    mainAxisAlignment: pw.MainAxisAlignment.start,
                    crossAxisAlignment: pw.CrossAxisAlignment.start,
                    children: [
                      pw.Column(children: [
                        pw.Text('____________'),
                        pw.Text('mario Alves Baiao Filho'.toUpperCase(),
                            style: assNomeTextStyle),
                        pw.Text('Secretaria de Gestão Pública',
                            style: assCargoTextStyle),
                      ]),
                      pw.SizedBox(width: 60),
                      pw.Column(children: [
                        pw.Text('____________'),
                        pw.Text('Marcelino Carlos Dias Borba'.toUpperCase(),
                            style: assNomeTextStyle),
                        pw.Text('Prefeito de Rio das Ostras',
                            style: assCargoTextStyle),
                      ])
                    ]),
              )
            ],
          )),
        ]); // Center
      }));

  var bytes = await pdf.save();
  await File('pdf_test.pdf').writeAsBytes(bytes);
}

Future<pw.ImageProvider> networkImage(String url,
    {Map<String, String>? headers}) async {
  final response = await http.get(Uri.parse(url), headers: headers);
  final bytes = response.bodyBytes;
  return pw.MemoryImage(bytes);
}

Future<String> networkSVG(String url, {Map<String, String>? headers}) async {
  final response = await http.get(Uri.parse(url), headers: headers);
  return response.body;
}

pdf_test.pdf

image

expected behavior

image

leocirto commented 1 month ago

You should remove the Row. You can replace the Container by a Padding too.

Try this one:

              // pw.Row(
                  // mainAxisAlignment: pw.MainAxisAlignment.start,
                  // crossAxisAlignment: pw.CrossAxisAlignment.start,
                  // children: [
                    // pw.Container(
                      // width: double.infinity,
                      // padding: pw.EdgeInsets.fromLTRB(150, 0, 0, 0),
                      // child:
                   pw.Padding(padding: pw.EdgeInsets.fromLTRB(150, 0, 50, 0),
                   child: 
                     pw.RichText(
                           textDirection: pw.TextDirection.ltr,
                           textAlign: pw.TextAlign.justify,
                          // softWrap: true,
                          maxLines: 10,
                          text: pw.TextSpan(
                              style: bodyTextStyle,
                              children: [
                                pw.TextSpan(
                                  style: strongTextStyle,
                                  text: 'Certificamos que ',
                                ),
                                pw.TextSpan(
                                  style: strongTextStyle,
                                  text: nomeCandidato.toUpperCase(),
                                ),
                                pw.TextSpan(
                                  style: bodyTextStyle,
                                  text: ' concluiu com aproveitamento o curso ',
                                ),
                                pw.TextSpan(
                                  style: strongTextStyle,
                                  text: nomeCurso.toUpperCase(),
                                ),
                                pw.TextSpan(
                                  style: bodyTextStyle,
                                  text: ' do Programa Municipal de Qualificação Profissional de Rio das Ostras, ministrado pela Secretaria de Gestão Pública, perfazendo um total de ',
                                ),
                                pw.TextSpan(
                                  style: strongTextStyle,
                                  text: '102 horas/aula.',
                                ),
                              ]) //) ),
                    ), ),

The output:

image

Note this in your code:

import 'package:pdf/pdf.dart'     as pw; // <-- pw HERE
import 'package:pdf/widgets.dart' as pw; // <-- pw HERE TOO
insinfo commented 1 month ago

Thanks for the help, that works, the only problem with this approach is that if you need to put something next to the text you will need a Row and then the problem will happen again.

leocirto commented 1 month ago

In this case I think you can use a WidgetSpan or a Row combined with Flexible.

For example:

          pw.Padding(
          padding: pw.EdgeInsets.fromLTRB(150, 0, 50, 0),
          child: 
            pw.RichText(
                  textDirection: pw.TextDirection.ltr,
                  textAlign: pw.TextAlign.justify,
                // softWrap: true,
                maxLines: 10,
                text: pw.TextSpan(
                    style: bodyTextStyle,
                    children: [
                      pw.WidgetSpan(
                        child: pw.Icon( pw.IconData( 0xe87d ) ),
                      ),
                      pw.WidgetSpan(
                        child: pw.Text( '  WidgetSpan  '),
                      ),

                      pw.TextSpan( style: strongTextStyle, text: 'Certificamos que ', ),
                      pw.TextSpan( style: strongTextStyle, text: nomeCandidato.toUpperCase(), ),
                      pw.TextSpan( style: bodyTextStyle, text: ' concluiu com aproveitamento o curso ', ),
                      pw.TextSpan( style: strongTextStyle, text: nomeCurso.toUpperCase(), ),
                      pw.TextSpan( style: bodyTextStyle, text: ' do Programa Municipal de Qualificação Profissional de Rio das Ostras, ministrado pela Secretaria de Gestão Pública, perfazendo um total de ', ),
                      pw.TextSpan( style: strongTextStyle, text: '102 horas/aula.', ),
                    ]) //) ),
          ), ),

          pw.SizedBox(height: 20),

          pw.Padding(  padding: pw.EdgeInsets.fromLTRB(100, 0, 50, 0),
          child: 
          pw.Row( children: [
            pw.Icon( pw.IconData( 0xe87d ) ),
            pw.Text( '  Text   '),
            pw.Flexible(child: 
            pw.RichText(
                textDirection: pw.TextDirection.ltr,
                textAlign: pw.TextAlign.justify,
                // softWrap: true,
                maxLines: 10,
                text: pw.TextSpan(
                    style: bodyTextStyle,
                    children: [
                      pw.TextSpan( style: strongTextStyle, text: 'Certificamos que ', ),
                      pw.TextSpan( style: strongTextStyle, text: nomeCandidato.toUpperCase(), ),
                      pw.TextSpan( style: bodyTextStyle, text: ' concluiu com aproveitamento o curso ', ),
                      pw.TextSpan( style: strongTextStyle, text: nomeCurso.toUpperCase(), ),
                      pw.TextSpan( style: bodyTextStyle, text: ' do Programa Municipal de Qualificação Profissional de Rio das Ostras, ministrado pela Secretaria de Gestão Pública, perfazendo um total de ', ),
                      pw.TextSpan( style: strongTextStyle, text: '102 horas/aula.', ),
                    ]) //) ),
          ), ), ] ), ),

Output:

image