Skip to content

Commit

Permalink
Merge pull request #357 from Ariemeth/print_pdf_options
Browse files Browse the repository at this point in the history
Added dialog to select which sections of the pdf to export/print
  • Loading branch information
Ariemeth authored Feb 6, 2024
2 parents 038452b + fd13776 commit 77fcecd
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 95 deletions.
178 changes: 103 additions & 75 deletions lib/screens/roster/pdf/pdf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:gearforce/screens/roster/pdf/record_sheet/record_sheet.dart';
import 'package:gearforce/screens/roster/pdf/record_sheet/rules_sheet.dart';
import 'package:gearforce/screens/roster/pdf/record_sheet/traits_sheet.dart';
import 'package:gearforce/screens/roster/pdf/unit_cards/unit_cards.dart';
import 'package:gearforce/widgets/pdf_settings.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
Expand All @@ -19,82 +20,132 @@ const double _bottomPageMargins = PdfPageFormat.inch / 28;
const double _unitCardMargins = 5.0 / 2.0;
const pw.EdgeInsets _footerMargin = const pw.EdgeInsets.only(top: 0);

Future<bool> printPDF(UnitRoster roster, {required String version}) async {
Future<bool> printPDF(
UnitRoster roster,
PDFSettings settings, {
required String version,
}) async {
// This is where we print the document
return Printing.layoutPdf(
// [onLayout] will be called multiple times
// when the user changes the printer or printer settings
onLayout: (PdfPageFormat format) {
// Any valid Pdf document can be returned here as a list of int
return buildPdf(format, roster, version: version);
return buildPdf(format, roster, settings, version: version);
},
);
}

Future<void> downloadPDF(
UnitRoster roster,
PDFSettings settings, {
required String version,
}) async {
final pdf = await buildPdf(
PdfPageFormat.letter,
roster,
settings,
version: version,
);

final String fileName = '${roster.name ?? _defaultRosterFileName}.pdf';
final saveLocation = await getSaveLocation(suggestedName: fileName);
if (saveLocation == null) {
// Operation was canceled by the user.
return;
}

final XFile textFile = XFile.fromData(
pdf,
mimeType: 'application/pdf',
name: fileName,
);

await textFile.saveTo(saveLocation.path);
}

/// This method takes a page format and generates the Pdf file data
Future<Uint8List> buildPdf(PdfPageFormat format, UnitRoster roster,
{required String version}) async {
Future<Uint8List> buildPdf(
PdfPageFormat format,
UnitRoster roster,
PDFSettings settings, {
required String version,
}) async {
// Create the Pdf document
final pw.Document doc = pw.Document();
final font = await PdfGoogleFonts.nunitoRegular();
final pageTheme = await _myPageTheme(format);

// Add record sheet summary
doc.addPage(
pw.MultiPage(
pageTheme: pageTheme,
build: (pw.Context context) {
return buildRecordSheet(font, roster);
},
footer: (pw.Context context) {
return _buildFooter(context, version, roster.rulesVersion);
}),
);
if (settings.sections.recordSheet) {
// Add record sheet summary
doc.addPage(
pw.MultiPage(
pageTheme: pageTheme,
build: (pw.Context context) {
return buildRecordSheet(font, roster);
},
footer: (pw.Context context) {
return _buildFooter(context, version, roster.rulesVersion);
}),
);
}

final unitCards = buildUnitCards(font, roster, version: version);

// Add unit cards
doc.addPage(pw.MultiPage(
pageTheme: pageTheme,
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.center,
build: (pw.Context context) {
return [
pw.Padding(
padding: pw.EdgeInsets.only(),
child: pw.Wrap(
children: unitCards,
spacing: _unitCardMargins * 2,
runSpacing: _unitCardMargins,
),
)
];
},
footer: (pw.Context context) {
return _buildFooter(context, version, roster.rulesVersion);
},
));
if (settings.sections.unitCards) {
final unitCards = buildUnitCards(font, roster, version: version);

// Add Trait reference page
doc.addPage(pw.MultiPage(
// Add unit cards
doc.addPage(pw.MultiPage(
pageTheme: pageTheme,
build: (context) {
return [buildTraitSheet(font, roster.getAllUnits())];
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.center,
build: (pw.Context context) {
return [
pw.Padding(
padding: pw.EdgeInsets.only(),
child: pw.Wrap(
children: unitCards,
spacing: _unitCardMargins * 2,
runSpacing: _unitCardMargins,
),
)
];
},
footer: (pw.Context context) {
return _buildFooter(context, version, roster.rulesVersion);
}));

// Add Rules reference page
doc.addPage(pw.MultiPage(
pageTheme: pageTheme,
build: (context) {
return [buildRulesSheet(font, roster)];
},
footer: (pw.Context context) {
return _buildFooter(context, version, roster.rulesVersion);
}));
));
}

if (settings.sections.traitReference) {
// Add Trait reference page
doc.addPage(pw.MultiPage(
pageTheme: pageTheme,
build: (context) {
return [buildTraitSheet(font, roster.getAllUnits())];
},
footer: (pw.Context context) {
return _buildFooter(context, version, roster.rulesVersion);
}));
}

if (settings.sections.factionRules || settings.sections.subFactionRules) {
// Add Rules reference page
doc.addPage(pw.MultiPage(
pageTheme: pageTheme,
build: (context) {
return [
buildRulesSheet(
font,
roster,
includeFactionRules: settings.sections.factionRules,
includeSubFactionRules: settings.sections.subFactionRules,
)
];
},
footer: (pw.Context context) {
return _buildFooter(context, version, roster.rulesVersion);
}));
}
// Build and return the final Pdf file data
return doc.save();
}
Expand Down Expand Up @@ -131,29 +182,6 @@ pw.Widget _buildFooter(
);
}

Future<void> downloadPDF(UnitRoster roster, {required String version}) async {
final pdf = await buildPdf(
PdfPageFormat.letter,
roster,
version: version,
);

final String fileName = '${roster.name ?? _defaultRosterFileName}.pdf';
final saveLocation = await getSaveLocation(suggestedName: fileName);
if (saveLocation == null) {
// Operation was canceled by the user.
return;
}

final XFile textFile = XFile.fromData(
pdf,
mimeType: 'application/pdf',
name: fileName,
);

await textFile.saveTo(saveLocation.path);
}

Future<pw.PageTheme> _myPageTheme(PdfPageFormat format) async {
final updatedFormat = format.copyWith(
marginLeft: _leftRightPageMargins,
Expand Down
102 changes: 102 additions & 0 deletions lib/screens/roster/pdf/pdf_settings_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import 'package:gearforce/widgets/pdf_settings.dart';

class PDFSettingsDialog extends StatelessWidget {
final String type;

const PDFSettingsDialog(this.type, {super.key});
@override
Widget build(BuildContext context) {
PDFSettings settings = PDFSettings();

return SimpleDialog(
title: Align(
child: Text('$type Settings', style: TextStyle(fontSize: 24)),
),
children: [
Text('Select the sections to include',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
)),
SettingsOptionLine(
text: 'Record Sheet',
onChanged: (bool? newValue) {
settings.sections.recordSheet = newValue!;
}),
SettingsOptionLine(
text: 'Unit Cards',
onChanged: (bool? newValue) {
settings.sections.unitCards = newValue!;
}),
SettingsOptionLine(
text: 'Traits',
onChanged: (bool? newValue) {
settings.sections.traitReference = newValue!;
}),
SettingsOptionLine(
text: 'Faction Rules',
onChanged: (bool? newValue) {
settings.sections.factionRules = newValue!;
}),
SettingsOptionLine(
text: 'Sub-Faction Rules',
onChanged: (bool? newValue) {
settings.sections.subFactionRules = newValue!;
}),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, settings);
},
child: Center(
child: Text(
type,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.green),
),
),
),
],
contentPadding: EdgeInsets.only(top: 10, bottom: 5, left: 10, right: 10),
titlePadding: EdgeInsets.only(top: 10, bottom: 0, left: 10, right: 10),
);
}
}

class SettingsOptionLine extends StatefulWidget {
final String text;
final ValueChanged<bool?> onChanged;

SettingsOptionLine({
required this.text,
required this.onChanged,
});

@override
State<SettingsOptionLine> createState() => _SettingsOptionLineState();
}

class _SettingsOptionLineState extends State<SettingsOptionLine> {
bool value = true;

@override
Widget build(BuildContext context) {
return Row(
children: [
Text(widget.text),
Spacer(),
Checkbox(
value: value,
onChanged: (bool? newValue) {
setState(() {
value = newValue!;
});
widget.onChanged(newValue);
},
),
],
);
}
}
15 changes: 9 additions & 6 deletions lib/screens/roster/pdf/record_sheet/header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ pw.Widget buildRosterHeader(pw.Font font, UnitRoster roster) {

return pw.Column(
children: [
pw.Center(
child: pw.Text(
_recordSheetHeadingText,
style: pw.TextStyle(
font: font,
fontSize: _recordSheetHeadingFontSize,
pw.Padding(
padding: pw.EdgeInsets.only(bottom: 5.0),
child: pw.Center(
child: pw.Text(
_recordSheetHeadingText,
style: pw.TextStyle(
font: font,
fontSize: _recordSheetHeadingFontSize,
),
),
),
),
Expand Down
28 changes: 18 additions & 10 deletions lib/screens/roster/pdf/record_sheet/rules_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,27 @@ import 'package:pdf/widgets.dart' as pw;
const double _headerTextSize = 12;
const double _standardTextSize = 10;

pw.Widget buildRulesSheet(pw.Font font, UnitRoster roster) {
// Faction rules
pw.Widget buildRulesSheet(
pw.Font font,
UnitRoster roster, {
bool includeFactionRules = true,
bool includeSubFactionRules = true,
}) {
final List<(String, String)> factionRules = [];
FactionRule.enabledRules(roster.rulesetNotifer.value.factionRules)
.forEach((fr) {
factionRules.add((fr.name, fr.description));
});
if (includeFactionRules) {
FactionRule.enabledRules(roster.rulesetNotifer.value.factionRules)
.forEach((fr) {
factionRules.add((fr.name, fr.description));
});
}

final List<(String, String)> subFactionRules = [];
FactionRule.enabledRules(roster.rulesetNotifer.value.subFactionRules)
.forEach((fr) {
subFactionRules.add((fr.name, fr.description));
});
if (includeSubFactionRules) {
FactionRule.enabledRules(roster.rulesetNotifer.value.subFactionRules)
.forEach((fr) {
subFactionRules.add((fr.name, fr.description));
});
}

final sheet = pw.Column(children: [
_buildRuleTable(font, ['Faction Rule', 'Description'], factionRules),
Expand Down
Loading

0 comments on commit 77fcecd

Please sign in to comment.