I have an app that prints a PDF report. The top of report has a header-section which has multiple columns with labels (1st, 3rd column) and values (2nd, 4th column). The basic structure is:
Everything worked fine with just english, but now we've translated the app for more languages and noticed the issue: for some languages (pt) the whole header section was empty i.e. nothing inside the first pw.Container was visible. After trying different things, I noticed that the issue was easily triggered off / on by making one of the labels a couple of characters shorter or longer. This indicates that overflowing text causes the whole area to disappear.
I tried to use OverflowBox and changing Text.overflow from visible to clip, but neither worked.
Here's a code that reproduces the problem. Removing XXXXXXXXXX from label "Bandwidth" makes header visible.
/// XXXXX TEST CODE
pageFmt = await PdfReport.getDefaultPageFmt();
// Set reasonable margins and landscape orientation
const double topMargin = 10;
const double bottomMargin = 10;
const double leftMargin = 14;
const double rightMargin = 17;
pageFmt = pageFmt!
.copyWith(
marginLeft: leftMargin * PdfPageFormat.mm,
marginRight: rightMargin * PdfPageFormat.mm,
marginTop: topMargin * PdfPageFormat.mm,
marginBottom: bottomMargin * PdfPageFormat.mm,
)
.landscape;
var note;
var startTime = DateTime.now();
var stopTime = DateTime.now().add(Duration(seconds: 123));
var durationStr = "123";
var bandwithStr = "unknown";
var textStyle = pw.TextStyle(fontSize: 8, font: pw.Font.helvetica());
pdf.addPage(pw.Page(
pageFormat: pageFmt,
build: (pw.Context context) {
return pw.Padding(
padding: const pw.EdgeInsets.only(
left: 5 * PdfPageFormat.mm, right: 1 * PdfPageFormat.mm),
child: pw.Container(
height: headerHeight * PdfPageFormat.mm,
child: pw.Column(children: [
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.SizedBox(
width: headerColumn1Width * PdfPageFormat.mm,
child: pw.Column(
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
"Recording ID:",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
pw.Text(
"Age:",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
pw.Text(
"Gender:",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
if (note != null && note.isNotEmpty)
pw.Text(
"Note:",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
],
)),
// Column 2. values for above
pw.SizedBox(
width: headerColumn2Width * PdfPageFormat.mm,
child: pw.Column(
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
"Recording 33",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
pw.Text(
"47",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
pw.Text(
"Male",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
if (note != null && note.isNotEmpty)
pw.Text(
note,
style: textStyle,
overflow: pw.TextOverflow.clip,
),
],
)),
// Column 3. titles for 4
pw.SizedBox(
width: headerColumn3Width * PdfPageFormat.mm,
child: pw.Column(
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
"Start time:",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
pw.Text(
"Stop time:",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
pw.Text(
"Duration:",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
pw.Text(
"Bandwidth XXXXXXXXXX:",
style: textStyle,
overflow: pw.TextOverflow.clip,
),
],
)),
// Column 4. values for above
pw.SizedBox(
width: headerColumn4Width * PdfPageFormat.mm,
child: pw.Column(
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
// TODO: Use localized date format(?) or just use "non-T" ISO8601 like now?
startTime
.toLocal()
.toIso8601String()
.split('.')
.first
.replaceFirst("T", " "),
style: textStyle,
),
pw.Text(
stopTime
.toLocal()
.toIso8601String()
.split('.')
.first
.replaceFirst("T", " "),
style: textStyle,
),
pw.Text(
durationStr,
style: textStyle,
),
pw.Text(
bandwithStr,
style: textStyle,
),
],
)),
// Column 5. Empty space
pw.Spacer(),
// Column 6. Time scale marker
pw.Container(
alignment: pw.Alignment.bottomCenter,
height: headerHeight * PdfPageFormat.mm,
width: 6.0 * ecgGraphXScalePtPerSec +
10 * PdfPageFormat.mm,
padding: const pw.EdgeInsets.only(
bottom: 1 * PdfPageFormat.mm,
right: 6 * PdfPageFormat.mm,
),
// decoration: pw.BoxDecoration(
// border: pw.Border.all(
// color: PdfColor.fromHex("#ff0000"),
// width: 0.2,
// ),
// ),
child: _drawTimeScaleMarker()),
// Column 7. AVG HR text on the right
pw.Container(
alignment: pw.Alignment.bottomCenter,
margin: const pw.EdgeInsets.only(
bottom: 1 * PdfPageFormat.mm),
height: (headerHeight - 1) * PdfPageFormat.mm,
width: 25 * PdfPageFormat.mm,
child: pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
pw.CustomPaint(
size: const PdfPoint(9 * PdfPageFormat.mm,
headerHeight * PdfPageFormat.mm),
painter: (canvas, size) {
canvas.setStrokeColor(PdfColors.black);
canvas.setLineWidth(ecgLineThicknessPt);
// Draw a pulse signal with 1mV amplitude
const pulseWidthPt = 3.0 * PdfPageFormat.mm;
double pulseHeight1mVPt = ecgRowHeight /
(2.0 * yRangeMillivolts) *
PdfPageFormat.mm;
canvas.moveTo(0, 0);
canvas.lineTo(pulseWidthPt, 0);
canvas.lineTo(pulseWidthPt, pulseHeight1mVPt);
canvas.lineTo(
2 * pulseWidthPt, pulseHeight1mVPt);
canvas.lineTo(2 * pulseWidthPt, 0);
canvas.lineTo(3 * pulseWidthPt, 0);
canvas.strokePath();
},
),
pw.Container(
margin: const pw.EdgeInsets.only(
bottom: 2 * PdfPageFormat.mm),
child: pw.Text("1 mV",
style: textStyle.copyWith(fontSize: 6))),
]),
),
pw.Container(
alignment: pw.Alignment.bottomCenter,
height: headerHeight * PdfPageFormat.mm,
child: pw.Text(
textAlign: pw.TextAlign.center,
"AVG\nHR",
style: textStyle.copyWith(
fontSize: 7,
),
)),
]),
pw.Expanded(child: pw.Divider(thickness: 0.5)),
])));
},
));
// Save the document.
final pdfFile2 = File(outputPath);
await pdf.saveFile(pdfFile2);
log.info("PDF file created. ${pdfFile2.lengthSync()} bytes.");
onProgress?.call(1.0);
return pdfFile2;
/// XXXXX TEST CODE
It's quite possible that I have misunderstood something about how the widgets work. Debugging into the code of dart_pdf did not reveal any exceptions or parts of code that would indicate "skipping rendering".
Describe the bug
I have an app that prints a PDF report. The top of report has a header-section which has multiple columns with labels (1st, 3rd column) and values (2nd, 4th column). The basic structure is:
etc.
Everything worked fine with just english, but now we've translated the app for more languages and noticed the issue: for some languages (pt) the whole header section was empty i.e. nothing inside the first pw.Container was visible. After trying different things, I noticed that the issue was easily triggered off / on by making one of the labels a couple of characters shorter or longer. This indicates that overflowing text causes the whole area to disappear.
I tried to use OverflowBox and changing Text.overflow from visible to clip, but neither worked.
To Reproduce
Here's a code that reproduces the problem. Removing XXXXXXXXXX from label "Bandwidth" makes header visible.
Expected behavior
The header column is visible even if some strings overflow to the next area.
Screenshots
Flutter Doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.38.4, on macOS 26.3.1 25D2128 darwin-arm64, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.3)
[✓] Chrome - develop for the web
[✓] Connected device (3 available)
! Error: Browsing on the local area network for Petri’s iPhone. Ensure the device is unlocked and
attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources
• No issues found!
Desktop (please complete the following information):
Smartphone (please complete the following information):
Additional context
It's quite possible that I have misunderstood something about how the widgets work. Debugging into the code of dart_pdf did not reveal any exceptions or parts of code that would indicate "skipping rendering".