From 00c19a1dbbf61508210c0c3983bc154d10f8e4dd Mon Sep 17 00:00:00 2001 From: KOOKIIEStudios <25145447+KOOKIIEStudios@users.noreply.github.com> Date: Mon, 13 Nov 2023 03:28:13 +0800 Subject: [PATCH] CHORE: Run Dart formatter --- README.md | 83 ++-- analysis_options.yaml | 4 +- lib/app.dart | 6 +- lib/constants.dart | 18 +- lib/main.dart | 4 +- lib/models/save_feedback.dart | 1 - lib/models/user.dart | 22 +- lib/providers/user_provider.dart | 4 +- lib/screens/buttons.dart | 1 + lib/screens/date_field.dart | 21 +- lib/screens/deck_field.dart | 10 +- lib/screens/division_radios.dart | 33 +- lib/screens/document.dart | 65 +-- lib/screens/home_material.dart | 12 +- lib/screens/id_field.dart | 5 +- lib/screens/name_field.dart | 8 +- lib/screens/open_file.dart | 6 +- lib/screens/open_in_explorer.dart | 6 +- lib/screens/paper_size_radios.dart | 16 +- lib/style.dart | 2 +- packages/deck_string_parser/CHANGELOG.md | 38 +- packages/deck_string_parser/README.md | 3 +- .../lib/deck_string_parser.dart | 16 +- packages/deck_string_parser/lib/src/card.dart | 2 +- packages/deck_string_parser/lib/src/deck.dart | 2 +- .../deck_string_parser/lib/src/energy.dart | 4 +- .../deck_string_parser/lib/src/parser.dart | 63 +-- .../deck_string_parser/lib/src/pokemon.dart | 2 +- .../lib/src/set_abbreviations.dart | 450 ++++++++++++------ .../deck_string_parser/lib/src/trainer.dart | 2 +- .../test/deck_string_parser_test.dart | 1 - .../deck_string_parser/test/parser_test.dart | 53 ++- packages/pokemon_pdf_builder/CHANGELOG.md | 24 +- packages/pokemon_pdf_builder/README.md | 9 +- .../example/pokemon_pdf_example.dart | 14 +- .../example/pokemon_pdf_letter_example.dart | 13 +- .../example/unfilled_form_example.pdf.dart | 5 +- .../pokemon_pdf_builder/lib/pokemon_pdf.dart | 2 +- .../pokemon_pdf_builder/lib/src/a4_form.dart | 2 +- .../lib/src/abstract_form.dart | 11 +- .../pokemon_pdf_builder/lib/src/division.dart | 2 +- .../lib/src/generic_form.dart | 18 +- .../lib/src/letter_form.dart | 2 +- .../pokemon_pdf_builder/lib/src/size.dart | 2 +- .../pokemon_pdf_builder/lib/src/utility.dart | 19 +- test/widget_test.dart | 3 +- 46 files changed, 685 insertions(+), 404 deletions(-) diff --git a/README.md b/README.md index 8d43ea0..0683cba 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # CastFORM + ![GitHub release (latest by date)](https://img.shields.io/github/v/release/BAA-Studios/CastFORM?display_name=tag&label=latest%20version) [![Code Style: Google](https://img.shields.io/badge/code%20style-google-blueviolet.svg)](https://dart.dev/guides/language/effective-dart/style) ![platform | windows](https://img.shields.io/badge/platform-windows-lightgrey) @@ -7,9 +8,14 @@ ![GitHub all releases](https://img.shields.io/github/downloads/BAA-Studios/CastFORM/total) **CastFORM** is an easy-to-use Pokémon TCG Deck Registration Sheet generator. -**CastFORM** offers an elegant graphical interface for filling in Pokémon deck lists for tournaments, and creates beautiful PDFs from user input. This reduces the likelihood of human errors associated with filling out of forms by hand, and also integrates well with existing players' workflows, given the popularity of deck strings (of various formats) in the Pokémon TCG community. +**CastFORM** offers an elegant graphical interface for filling in Pokémon deck lists for tournaments, and creates +beautiful PDFs from user input. This reduces the likelihood of human errors associated with filling out of forms by +hand, and also integrates well with existing players' workflows, given the popularity of deck strings (of various +formats) in the Pokémon TCG community. -This project is based on [Brandon Nguyen's](https://github.com/Bratah123) CLI Python script that served a similar purpose. **CastFORM** was spearheaded in response to the high demand for a user-friendly program in his local TCG community, most of whom were not comfortable with CLI tools. +This project is based on [Brandon Nguyen's](https://github.com/Bratah123) CLI Python script that served a similar +purpose. **CastFORM** was spearheaded in response to the high demand for a user-friendly program in his local TCG +community, most of whom were not comfortable with CLI tools. ## Gallery @@ -20,71 +26,86 @@ This project is based on [Brandon Nguyen's](https://github.com/Bratah123) CLI Py ![dark mode screenshot](https://user-images.githubusercontent.com/25145447/232040394-cb3da909-e51b-4736-9497-ee7be42641ce.png) ## Installation & Usage Instructions + *Note: Only 64-bit Windows 11 machines are officially supported.* ### ZIP Archive + 1. Download `castform.zip` from the releases page - - Note: actual file name may vary + - Note: actual file name may vary 2. Unzip it into your desired install location 3. Run `CastFORM.exe` from that location ### Windows Installer + 1. Download `CastFORM_x64.exe` from the releases page 2. Run it **as administrator** to install CastFORM to `C:\Program Files\CastFORM` 3. Run `CastFORM.exe` from `C:\Program Files\CastFORM\CastFORM.exe`, and/or create a shortcut for it ### Video Instructions + - A video of the installation and usage steps: https://youtu.be/a4peYpnhHg0 --- ## Technical Information -**CastFORM** is built with **Flutter 3.13.9** for **64-bit Windows 11**, and targets [**Google's Dart style guide**](https://dart.dev/guides/language/effective-dart/style). + +**CastFORM** is built with **Flutter 3.13.9** for **64-bit Windows 11**, and targets [**Google's Dart style guide +**](https://dart.dev/guides/language/effective-dart/style). For testing, we aim to provide complete coverage for API behaviour internally by release. ### Development Environment Set-up + 1. [Install Flutter](https://docs.flutter.dev/get-started/install) and add it to PATH 2. Clone the repository 3. Open the repository in IntelliJ - You may use your IDE of choice, but we prefer IntelliJ here at BAA Studios - Make sure you install the Dart and Flutter Plugin for IntelliJ 4. Make sure `Windows (desktop)` is selected in the list of devices - - The location of the button differs depending on IntelliJ version - - For IntelliJ 2022.3 use the drop-down menu at the top right: - ![illustration of where the menu is found](https://i.imgur.com/kqMsy3g.png) + - The location of the button differs depending on IntelliJ version + - For IntelliJ 2022.3 use the drop-down menu at the top right: + ![illustration of where the menu is found](https://i.imgur.com/kqMsy3g.png) 5. Hit the `Run` button at the top right of the window - The location of the button differs depending on IntelliJ version - For IntelliJ 2022.3 click here: - ![illustration of where to click](https://i.imgur.com/0FGpLNN.png) + ![illustration of where to click](https://i.imgur.com/0FGpLNN.png) ### Internal Package API + API docs for the business logic are included as static HTML files in the repository. -After cloning the repository, navigate to the respective package to open them: +After cloning the repository, navigate to the respective package to open them: + - `packages/deck_string_parser/doc/api/index.html` - `packages/pokemon_pdf_builder/doc/api/index.html` ### Toolchain + A number of standalone Python scripts have been make to automate metadata fetching and template creation: + - [ForeCAST](https://github.com/KOOKIIEStudios/Forecast) - - Uses Bulbapedia's REST APIs to get all known set abbreviations + - Uses Bulbapedia's REST APIs to get all known set abbreviations - [WEATHERBall](https://github.com/KOOKIIEStudios/Weather-Ball) - - Remote mode: Scrapes the official Pokémon website for the registration form templates - - Local mode: Converts PDF files to WebP - - Added as a fallback option upon discovery that Pokémon uses Incapsula as an anti-scraping measure + - Remote mode: Scrapes the official Pokémon website for the registration form templates + - Local mode: Converts PDF files to WebP + - Added as a fallback option upon discovery that Pokémon uses Incapsula as an anti-scraping measure ### Build Instructions + 1. Run `flutter build windows` 2. Navigate to the output folder `/build/windows/runner/Release` -3. Add the following `dlls` (which can be found in a Visual Studio 2022 installation folder, e.g. `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\14.32.31326\x64\Microsoft.VC143.CRT`) - - msvcp140.dll - - vcruntime140.dll - - vcruntime140_1.dll +3. Add the following `dlls` (which can be found in a Visual Studio 2022 installation folder, + e.g. `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\14.32.31326\x64\Microsoft.VC143.CRT`) + - msvcp140.dll + - vcruntime140.dll + - vcruntime140_1.dll 4. Package the contents of this folder as a ZIP file - - Users can unzip to their desired location and run `CastFORM.exe` from there + - Users can unzip to their desired location and run `CastFORM.exe` from there ### Bundle with Windows Installer (Optional) + *Note: This assumes you've already run through steps 1-4 of the build instructions* -**First time bundling:** +**First time bundling:** + 1. Add a batch script `setup.bat` (refer to `/sample_setup.bat` for template) 2. Run `iexpress.exe` as administrator 3. Select `Create new Self Extraction Directive file`, and hit `Next` @@ -93,13 +114,14 @@ A number of standalone Python scripts have been make to automate metadata fetchi 6. Select `No prompt`, and hit `Next` 7. Select `Do not display a license`, and hit `Next` 8. Use the `Add` button to add all the files in the output folder from above, and hit `Next` - - The file picker dialog box allows `CTRL + A` to select all files - - The file picker dialog box ignores folders, and does not add them recursively - - You may use `CTRL + A` on the output folder and repeat for all nested folders to quickly add everything + - The file picker dialog box allows `CTRL + A` to select all files + - The file picker dialog box ignores folders, and does not add them recursively + - You may use `CTRL + A` on the output folder and repeat for all nested folders to quickly add everything 9. In the `Install Program` field, click on it and manually replace with `cmd /c setup.bat`, and hit `Next` - - You are able to type in this drop down box - - The `cmd /c` part is **REQUIRED** - - IExpress simply bundles/extracts as well as wrap around a batch script, so all installation logic should be written in `setup.bat` + - You are able to type in this drop down box + - The `cmd /c` part is **REQUIRED** + - IExpress simply bundles/extracts as well as wrap around a batch script, so all installation logic should be + written in `setup.bat` 10. Select `Default`, and hit `Next` 11. Input a message if you like, and hit `Next` 12. Click `Browse` and select an output folder for the resulting installer EXE, and give it a name @@ -111,12 +133,19 @@ A number of standalone Python scripts have been make to automate metadata fetchi 16. On the `Create package` page, hit `Next` to start the bundling process, and then `Finish` once it's done **For subsequent bundling:** + 1. Run `iexpress.exe` as administrator 2. Select `Open existing Self Extraction Directive file`, and select the SED file with `Browse` 3. Select `Create Package` -4. On the `Create package` page, hit `Next` to start the bundling process, and then `Finish` once it's done +4. On the `Create package` page, hit `Next` to start the bundling process, and then `Finish` once it's done --- ## Disclaimer -**CastFORM** is an open-source program for a generating a niche type of PDF documents. **CastFORM** is not affiliated with Nintendo, The Pokémon Company, or any Pokémon-related organisations fan-driven or otherwise. **CastFORM** is non-monetised, and provided as is. Every reasonable effort has been taken to ensure correctness and reliability of **CastFORM**. We will not be liable for any special, direct, indirect, or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action if contract, negligence or other tortious action, arising out of or in connection with the use of **CastFORM** (in part or in whole). + +**CastFORM** is an open-source program for a generating a niche type of PDF documents. **CastFORM** is not affiliated +with Nintendo, The Pokémon Company, or any Pokémon-related organisations fan-driven or otherwise. **CastFORM** is +non-monetised, and provided as is. Every reasonable effort has been taken to ensure correctness and reliability of * +*CastFORM**. We will not be liable for any special, direct, indirect, or consequential damages or any damages whatsoever +resulting from loss of use, data or profits, whether in an action if contract, negligence or other tortious action, +arising out of or in connection with the use of **CastFORM** (in part or in whole). diff --git a/analysis_options.yaml b/analysis_options.yaml index 61b6c4d..ae08714 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -22,8 +22,8 @@ linter: # `// ignore_for_file: name_of_lint` syntax on the line or in the file # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/lib/app.dart b/lib/app.dart index 265264a..e066042 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,7 +1,7 @@ -import 'package:flutter/material.dart'; import 'package:castform/constants.dart' show initPackageInfo, initPdfConstants; import 'package:castform/screens/home_material.dart'; import 'package:castform/style.dart'; +import 'package:flutter/material.dart'; class App extends StatelessWidget { const App({super.key}); @@ -14,8 +14,8 @@ class App extends StatelessWidget { return MaterialApp( theme: theme, darkTheme: darkTheme, - home: const HomeMaterial(), // Google-style for Windows + home: const HomeMaterial(), // Google-style for Windows // home: HomeCupertino(), // Apple-style for macOS ); } -} \ No newline at end of file +} diff --git a/lib/constants.dart b/lib/constants.dart index 905c31e..2344a41 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:pdf/widgets.dart' as pw; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:pdf/widgets.dart' as pw; import 'package:printing/printing.dart'; enum PaperType { a4, letter } @@ -15,7 +15,6 @@ const defaultBorder = OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(8.0)), ); - pw.TextStyle? formTextStyle; pw.TextStyle? unicodeTextStyle; pw.Image? a4FormTemplate; @@ -25,9 +24,7 @@ void initPdfConstants() { PdfGoogleFonts.mPLUS1pMedium().then((value) { unicodeTextStyle = pw.TextStyle(font: value, fontSize: 15); }); - rootBundle - .load("assets/fonts/RobotoSlab-Regular.ttf") - .then((value) { + rootBundle.load("assets/fonts/RobotoSlab-Regular.ttf").then((value) { pw.Font font = pw.Font.ttf(value); formTextStyle = pw.TextStyle(font: font, fontSize: 10.0); }); @@ -55,9 +52,8 @@ void initPackageInfo() { const aboutText = Text( "CastFORM is a free and easy to use tool for automatic filling out of Pokemon " - "tournament registration sheets!\n" - "\n" - "Put together with love from Brandon Nguyen and Amos Chua of BAA Studios!\n" - "If you enjoy our work, do follow us on GitHub; if you're feeling generous, you can " - "buy us a coffee on ko-fi too!" -); \ No newline at end of file + "tournament registration sheets!\n" + "\n" + "Put together with love from Brandon Nguyen and Amos Chua of BAA Studios!\n" + "If you enjoy our work, do follow us on GitHub; if you're feeling generous, you can " + "buy us a coffee on ko-fi too!"); diff --git a/lib/main.dart b/lib/main.dart index f0ce9fa..f4ec89f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart'; import 'package:castform/app.dart'; +import 'package:flutter/material.dart'; -void main() => runApp(const App()); \ No newline at end of file +void main() => runApp(const App()); diff --git a/lib/models/save_feedback.dart b/lib/models/save_feedback.dart index 7b19d45..600afbe 100644 --- a/lib/models/save_feedback.dart +++ b/lib/models/save_feedback.dart @@ -25,4 +25,3 @@ class SaveResponse { ); } } - diff --git a/lib/models/user.dart b/lib/models/user.dart index 5cf69de..5fb8d69 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -31,14 +31,16 @@ class User { // Deck validation String cachedDeckString = deckString ?? ""; if (!isValidDeckString(cachedDeckString)) { - return const SaveResponse(notificationText: "Deck does not contain 60 cards!", isError: true); + return const SaveResponse( + notificationText: "Deck does not contain 60 cards!", isError: true); } // Open save-as dialog, which gives us the full save path as string var dateTime = DateTime.now(); String? outputFilePath = await FilePicker.platform.saveFile( dialogTitle: "Please select an output file:", - fileName: "pokemon_registration_sheet_${dateTime.month}${dateTime.day}${dateTime.second}.pdf", + fileName: + "pokemon_registration_sheet_${dateTime.month}${dateTime.day}${dateTime.second}.pdf", ); if (outputFilePath == null) { @@ -78,18 +80,21 @@ class User { // Export as PDF try { await File(outputFilePath).writeAsBytes(await formHandler.buildPdf()); - } catch(_) { - return const SaveResponse(notificationText: "Unable to export as PDF!", isError: true); + } catch (_) { + return const SaveResponse( + notificationText: "Unable to export as PDF!", isError: true); } // show in Explorer if (openInExplorer ?? false) { // strip the trailing file name var temp = outputFilePath.split("\\"); - var directoryPath = "file:/${temp.sublist(0, temp.length - 1).join("\\")}"; + var directoryPath = + "file:/${temp.sublist(0, temp.length - 1).join("\\")}"; final Uri uri = Uri.parse(directoryPath); if (!await launchUrl(uri)) { - return const SaveResponse(notificationText: "Unable to open in Explorer!", isError: true); + return const SaveResponse( + notificationText: "Unable to open in Explorer!", isError: true); } } @@ -97,9 +102,10 @@ class User { if (openInViewer ?? false) { final Uri uri = Uri.file(outputFilePath); if (!await launchUrl(uri)) { - return const SaveResponse(notificationText: "Unable to open output file!", isError: true); + return const SaveResponse( + notificationText: "Unable to open output file!", isError: true); } } return const SaveResponse(notificationText: "Successfully saved as PDF!"); } -} \ No newline at end of file +} diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart index 68e4ed6..d019418 100644 --- a/lib/providers/user_provider.dart +++ b/lib/providers/user_provider.dart @@ -1,6 +1,6 @@ -import 'package:flutter/foundation.dart'; import 'package:castform/constants.dart'; import 'package:castform/models/user.dart'; +import 'package:flutter/foundation.dart'; import 'package:pokemon_pdf_builder/pokemon_pdf.dart'; class UserProvider extends User with ChangeNotifier { @@ -43,4 +43,4 @@ class UserProvider extends User with ChangeNotifier { openInViewer = !(openInViewer ?? false); notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/screens/buttons.dart b/lib/screens/buttons.dart index cd6ab5e..5bf88fd 100644 --- a/lib/screens/buttons.dart +++ b/lib/screens/buttons.dart @@ -5,6 +5,7 @@ import 'package:castform/providers/user_provider.dart'; class SaveButton extends StatelessWidget { final GlobalKey formKey; + const SaveButton({super.key, required this.formKey}); @override diff --git a/lib/screens/date_field.dart b/lib/screens/date_field.dart index 46ffff0..15e3741 100644 --- a/lib/screens/date_field.dart +++ b/lib/screens/date_field.dart @@ -1,7 +1,7 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:castform/constants.dart'; import 'package:castform/providers/user_provider.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class DateField extends StatefulWidget { const DateField({super.key}); @@ -11,8 +11,9 @@ class DateField extends StatefulWidget { } class _DateFieldState extends State { - final _dateController = TextEditingController(); // Overriding for custom date handling - + final _dateController = + TextEditingController(); // Overriding for custom date handling + @override Widget build(BuildContext context) { return TextFormField( @@ -21,22 +22,26 @@ class _DateFieldState extends State { decoration: const InputDecoration( labelText: "Date of Birth (Optional)", ), - onTap: () async { // valid date from now to 125 years ago + onTap: () async { + // valid date from now to 125 years ago await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime.now().subtract(maximumHumanLifespan), lastDate: DateTime.now(), ).then((DateTime? value) { - if (value != null) { // Fill the field if input is valid + if (value != null) { + // Fill the field if input is valid _dateController.text = "${value.month}/${value.day}/${value.year}"; - } else { // Clear the field, if "cancel" is clicked + } else { + // Clear the field, if "cancel" is clicked _dateController.text = ""; } context.read().setDate(_dateController.text); }); }, - onSaved: (_) => context.read().setDate(_dateController.text), + onSaved: (_) => + context.read().setDate(_dateController.text), ); } } diff --git a/lib/screens/deck_field.dart b/lib/screens/deck_field.dart index 76ab78c..9116cf9 100644 --- a/lib/screens/deck_field.dart +++ b/lib/screens/deck_field.dart @@ -1,7 +1,7 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:castform/constants.dart'; import 'package:castform/providers/user_provider.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class DeckField extends StatefulWidget { const DeckField({super.key}); @@ -39,8 +39,10 @@ class _DeckFieldState extends State { maxLines: null, expands: true, keyboardType: TextInputType.multiline, - onChanged: (String? value) => context.read().setDeckString(value), - onSaved: (String? value) => context.read().setDeckString(value), + onChanged: (String? value) => + context.read().setDeckString(value), + onSaved: (String? value) => + context.read().setDeckString(value), ); } } diff --git a/lib/screens/division_radios.dart b/lib/screens/division_radios.dart index 41b5485..cf059bc 100644 --- a/lib/screens/division_radios.dart +++ b/lib/screens/division_radios.dart @@ -1,9 +1,8 @@ +import 'package:castform/providers/user_provider.dart'; import 'package:flutter/material.dart'; import 'package:pokemon_pdf_builder/pokemon_pdf.dart'; import 'package:provider/provider.dart'; -import 'package:castform/providers/user_provider.dart'; - class AutoRadio extends StatelessWidget { const AutoRadio({super.key}); @@ -12,8 +11,10 @@ class AutoRadio extends StatelessWidget { return RadioListTile( title: const Text("Auto"), value: Division.auto, - groupValue: context.select((userProvider) => userProvider.division), - onChanged: (Division? value) => context.read().setDivision(value), + groupValue: context.select( + (userProvider) => userProvider.division), + onChanged: (Division? value) => + context.read().setDivision(value), dense: true, ); } @@ -27,8 +28,10 @@ class JuniorRadio extends StatelessWidget { return RadioListTile( title: const Text("Junior"), value: Division.junior, - groupValue: context.select((userProvider) => userProvider.division), - onChanged: (Division? value) => context.read().setDivision(value), + groupValue: context.select( + (userProvider) => userProvider.division), + onChanged: (Division? value) => + context.read().setDivision(value), dense: true, ); } @@ -42,8 +45,10 @@ class SeniorRadio extends StatelessWidget { return RadioListTile( title: const Text("Senior"), value: Division.senior, - groupValue: context.select((userProvider) => userProvider.division), - onChanged: (Division? value) => context.read().setDivision(value), + groupValue: context.select( + (userProvider) => userProvider.division), + onChanged: (Division? value) => + context.read().setDivision(value), dense: true, ); } @@ -57,8 +62,10 @@ class MastersRadio extends StatelessWidget { return RadioListTile( title: const Text("Masters"), value: Division.masters, - groupValue: context.select((userProvider) => userProvider.division), - onChanged: (Division? value) => context.read().setDivision(value), + groupValue: context.select( + (userProvider) => userProvider.division), + onChanged: (Division? value) => + context.read().setDivision(value), dense: true, ); } @@ -72,8 +79,10 @@ class NoneRadio extends StatelessWidget { return RadioListTile( title: const Text("None"), value: Division.none, - groupValue: context.select((userProvider) => userProvider.division), - onChanged: (Division? value) => context.read().setDivision(value), + groupValue: context.select( + (userProvider) => userProvider.division), + onChanged: (Division? value) => + context.read().setDivision(value), dense: true, ); } diff --git a/lib/screens/document.dart b/lib/screens/document.dart index 5d3a4c8..8bba53a 100644 --- a/lib/screens/document.dart +++ b/lib/screens/document.dart @@ -1,10 +1,10 @@ -import 'package:flutter/material.dart'; -import 'package:printing/printing.dart'; -import 'package:provider/provider.dart'; import 'package:castform/constants.dart'; import 'package:castform/providers/user_provider.dart'; import 'package:deck_string_parser/deck_string_parser.dart'; +import 'package:flutter/material.dart'; import 'package:pokemon_pdf_builder/pokemon_pdf.dart'; +import 'package:printing/printing.dart'; +import 'package:provider/provider.dart'; class Document extends StatefulWidget { const Document({super.key}); @@ -16,36 +16,47 @@ class Document extends StatefulWidget { class _DocumentState extends State { @override Widget build(BuildContext context) { - String deckString = context.select((userProvider) => userProvider.deckString) ?? ""; - String name = context.select((userProvider) => userProvider.playerName) ?? ""; - String playerId = context.select((userProvider) => userProvider.playerId) ?? ""; - String dateOfBirth = context.select((userProvider) => userProvider.dateOfBirth) ?? ""; - Division division = context.select((userProvider) => userProvider.division) ?? Division.none; + String deckString = context.select( + (userProvider) => userProvider.deckString) ?? + ""; + String name = context.select( + (userProvider) => userProvider.playerName) ?? + ""; + String playerId = context.select( + (userProvider) => userProvider.playerId) ?? + ""; + String dateOfBirth = context.select( + (userProvider) => userProvider.dateOfBirth) ?? + ""; + Division division = context.select( + (userProvider) => userProvider.division) ?? + Division.none; /*String deckString = context.watch().deckString ?? ""; String name = context.watch().playerName ?? ""; String playerId = context.watch().playerId ?? ""; String dateOfBirth = context.watch().dateOfBirth ?? "";*/ - return PdfPreview(build: (_) { - // Create a fresh render every time a change is detected - // Preview in A4 only - Deck? deck; - if (deckString.isNotEmpty && isValidDeckString(deckString)) { - deck = parseDeck(deckString); - } - A4Form document = A4Form( - formTemplate: a4FormTemplate, - textStyle: formTextStyle, - unicodeTextStyle: unicodeTextStyle, - name: name, - playerId: playerId, - dateOfBirth: dateOfBirth, - division: division, - deck: deck, - ); + return PdfPreview( + build: (_) { + // Create a fresh render every time a change is detected + // Preview in A4 only + Deck? deck; + if (deckString.isNotEmpty && isValidDeckString(deckString)) { + deck = parseDeck(deckString); + } + A4Form document = A4Form( + formTemplate: a4FormTemplate, + textStyle: formTextStyle, + unicodeTextStyle: unicodeTextStyle, + name: name, + playerId: playerId, + dateOfBirth: dateOfBirth, + division: division, + deck: deck, + ); - return document.buildPdf(); - }, + return document.buildPdf(); + }, allowPrinting: false, allowSharing: false, canChangePageFormat: false, diff --git a/lib/screens/home_material.dart b/lib/screens/home_material.dart index e07a76e..c6b9b45 100644 --- a/lib/screens/home_material.dart +++ b/lib/screens/home_material.dart @@ -1,10 +1,10 @@ import 'dart:io' show Platform; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; + import 'package:castform/providers/user_provider.dart'; import 'package:castform/screens/document.dart'; import 'package:castform/screens/form_column.dart'; - +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; /// Holds the UI layout for the home page, with Material design class HomeMaterial extends StatefulWidget { @@ -35,7 +35,8 @@ StatelessWidget getHomeScreen() { if (Platform.isAndroid) { return const AndroidScreen(); } - throw UnsupportedError("This app is only intended to run on Windows, Ubuntu, or Android"); + throw UnsupportedError( + "This app is only intended to run on Windows, Ubuntu, or Android"); } class AndroidScreen extends StatelessWidget { @@ -53,7 +54,8 @@ class WindowsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return const Row( // Left side for forms; right side for live preview + return const Row( + // Left side for forms; right side for live preview children: [ Flexible( flex: 6, diff --git a/lib/screens/id_field.dart b/lib/screens/id_field.dart index 88242f5..21d9efe 100644 --- a/lib/screens/id_field.dart +++ b/lib/screens/id_field.dart @@ -1,6 +1,6 @@ +import 'package:castform/providers/user_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:castform/providers/user_provider.dart'; class IdField extends StatefulWidget { const IdField({super.key}); @@ -22,7 +22,8 @@ class _IdFieldState extends State { ), onSaved: (_) => context.read().setId(_idController.text), ), - onFocusChange: (_) => context.read().setId(_idController.text), + onFocusChange: (_) => + context.read().setId(_idController.text), ); } } diff --git a/lib/screens/name_field.dart b/lib/screens/name_field.dart index bd29c0e..717e213 100644 --- a/lib/screens/name_field.dart +++ b/lib/screens/name_field.dart @@ -1,6 +1,6 @@ +import 'package:castform/providers/user_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:castform/providers/user_provider.dart'; class NameField extends StatefulWidget { const NameField({super.key}); @@ -26,9 +26,11 @@ class _NameFieldState extends State { } return null; }, - onSaved: (_) => context.read().setName(_nameController.text), + onSaved: (_) => + context.read().setName(_nameController.text), ), - onFocusChange: (_) => context.read().setName(_nameController.text), + onFocusChange: (_) => + context.read().setName(_nameController.text), ); } } diff --git a/lib/screens/open_file.dart b/lib/screens/open_file.dart index 60c03b3..9b89fe1 100644 --- a/lib/screens/open_file.dart +++ b/lib/screens/open_file.dart @@ -1,6 +1,6 @@ +import 'package:castform/providers/user_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:castform/providers/user_provider.dart'; class OpenFileOnSave extends StatelessWidget { const OpenFileOnSave({super.key}); @@ -13,7 +13,9 @@ class OpenFileOnSave extends StatelessWidget { textAlign: TextAlign.start, ), dense: true, - value: context.select((userProvider) => userProvider.openInViewer) ?? false, + value: context.select( + (userProvider) => userProvider.openInViewer) ?? + false, onChanged: (_) => context.read().toggleOpenFile(), ); } diff --git a/lib/screens/open_in_explorer.dart b/lib/screens/open_in_explorer.dart index 7f74466..c6ad46c 100644 --- a/lib/screens/open_in_explorer.dart +++ b/lib/screens/open_in_explorer.dart @@ -1,6 +1,6 @@ +import 'package:castform/providers/user_provider.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:castform/providers/user_provider.dart'; class OpenInExplorer extends StatelessWidget { const OpenInExplorer({super.key}); @@ -13,7 +13,9 @@ class OpenInExplorer extends StatelessWidget { textAlign: TextAlign.start, ), dense: true, - value: context.select((userProvider) => userProvider.openInExplorer) ?? false, + value: context.select( + (userProvider) => userProvider.openInExplorer) ?? + false, onChanged: (_) => context.read().toggleOpenInExplorer(), ); } diff --git a/lib/screens/paper_size_radios.dart b/lib/screens/paper_size_radios.dart index c3504d8..12cf0da 100644 --- a/lib/screens/paper_size_radios.dart +++ b/lib/screens/paper_size_radios.dart @@ -1,7 +1,7 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:castform/constants.dart'; import 'package:castform/providers/user_provider.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class A4Radio extends StatelessWidget { const A4Radio({super.key}); @@ -11,8 +11,10 @@ class A4Radio extends StatelessWidget { return RadioListTile( title: const Text("A4"), value: PaperType.a4, - groupValue: context.select((userProvider) => userProvider.paperType), - onChanged: (PaperType? value) => context.read().setPaperType(value), + groupValue: context.select( + (userProvider) => userProvider.paperType), + onChanged: (PaperType? value) => + context.read().setPaperType(value), dense: true, ); } @@ -26,8 +28,10 @@ class LetterRadio extends StatelessWidget { return RadioListTile( title: const Text("Letter"), value: PaperType.letter, - groupValue: context.select((userProvider) => userProvider.paperType), - onChanged: (PaperType? value) => context.read().setPaperType(value), + groupValue: context.select( + (userProvider) => userProvider.paperType), + onChanged: (PaperType? value) => + context.read().setPaperType(value), dense: true, ); } diff --git a/lib/style.dart b/lib/style.dart index 1f58066..56cb2d6 100644 --- a/lib/style.dart +++ b/lib/style.dart @@ -16,4 +16,4 @@ final darkTheme = ThemeData( colorSchemeSeed: Colors.indigo, brightness: Brightness.dark, useMaterial3: true, -); \ No newline at end of file +); diff --git a/packages/deck_string_parser/CHANGELOG.md b/packages/deck_string_parser/CHANGELOG.md index 01d2f23..88c5683 100644 --- a/packages/deck_string_parser/CHANGELOG.md +++ b/packages/deck_string_parser/CHANGELOG.md @@ -1,51 +1,63 @@ ## 1.4.1 + - Update dependencies ## 1.4.0 + - Add support up to Pokemon Trading Card Game Classic - - Includes support for Paradox Rift + - Includes support for Paradox Rift - Migrate to new ForeCAST-based tooling for set abbreviations ## 1.3.2 + - Add support for trailing `PH` in PTCGL representation of full/alt art trainer cards' names - Merge similar energy cards into the same entry ## 1.3.1 + - Routine updating of list of known sets (via Bulbapedia as of 9th July 2023) ## 1.3.0 + - Add `PAL` to list of known sets - Add support for PTCGL tokens ## 1.2.0 -- Holographic cards (sometimes denoted with a trailing `PH`) will now automatically merge into existing lines with otherwise non-holographic cards - - Similar to same-name trainer cards in v1.1.0 + +- Holographic cards (sometimes denoted with a trailing `PH`) will now automatically merge into existing lines with + otherwise non-holographic cards + - Similar to same-name trainer cards in v1.1.0 - Add try-parse to public API, so allow for deck string validation ## 1.1.0 Feature Update + ### New Features + - Merge same-name trainer cards into the same entry - - Sometimes the same trainers can appear in different sets; for competition purposes, these are considered equal + - Sometimes the same trainers can appear in different sets; for competition purposes, these are considered equal - Add function to check the total number of cards in the deck - LimitlessTCG format support - Expand unit test comprehensiveness - - Check same-name trainer card mergers - - Check deck validation functionality - - Expand current test suite to include similar tests for LimitlessTCG format + - Check same-name trainer card mergers + - Check deck validation functionality + - Expand current test suite to include similar tests for LimitlessTCG format ### Bug Fixes + - Handling for LimitlessTCG non-header format - - Erroneously returning empty buffer - - Stray negation in conditional (resulting in checking for the opposite condition) + - Erroneously returning empty buffer + - Stray negation in conditional (resulting in checking for the opposite condition) - Missing delimiter in unit test variable ## 1.0.0 Feature Update + ### New Features + - Implement core parsing functionality (MVP milestone 1) - - PTCGO format support - - PTCGL format support - - Strips out `PH` tag for indicating holo cards - - Strips out trailing card tally + - PTCGO format support + - PTCGL format support + - Strips out `PH` tag for indicating holo cards + - Strips out trailing card tally - Implement full unit test coverage for core parsing functionality (MVP milestone 2) - Example code provided (MVP milestone 3) diff --git a/packages/deck_string_parser/README.md b/packages/deck_string_parser/README.md index 28007cd..161fc35 100644 --- a/packages/deck_string_parser/README.md +++ b/packages/deck_string_parser/README.md @@ -2,7 +2,8 @@ This package houses the business logic for parsing deck strings provided by user ## Features -PTCGO, PTCGL, and LimitlessTCG deck strings will be processed into `Deck` objects, which have `Pokemon`, `Trainer`, and `Energy` object +PTCGO, PTCGL, and LimitlessTCG deck strings will be processed into `Deck` objects, which have `Pokemon`, `Trainer`, +and `Energy` object lists as attribute. These in turn have the relevant data (e.g. name and quantity) as attribute. diff --git a/packages/deck_string_parser/lib/deck_string_parser.dart b/packages/deck_string_parser/lib/deck_string_parser.dart index 53028f9..0b4db40 100644 --- a/packages/deck_string_parser/lib/deck_string_parser.dart +++ b/packages/deck_string_parser/lib/deck_string_parser.dart @@ -5,17 +5,17 @@ import 'package:deck_string_parser/src/deck.dart'; import 'package:deck_string_parser/src/parser.dart'; export 'src/card.dart'; +export 'src/deck.dart'; export 'src/energy.dart'; export 'src/pokemon.dart'; export 'src/trainer.dart'; -export 'src/deck.dart'; Deck parseDeck(String deckString) { var crudeDeckList = stripHeaders(crudeSplit(deckString)); return Deck( - pokemonList: parsePokemonCards(crudeDeckList), - trainerList: parseTrainerCards(crudeDeckList), - energyList: parseEnergyCards(crudeDeckList), + pokemonList: parsePokemonCards(crudeDeckList), + trainerList: parseTrainerCards(crudeDeckList), + energyList: parseEnergyCards(crudeDeckList), ); } @@ -26,11 +26,11 @@ bool isValidDeckCount(Deck deck) { quantity += int.parse(element.quantity); } - for(var element in deck.trainerList) { + for (var element in deck.trainerList) { quantity += int.parse(element.quantity); } - for(var element in deck.energyList) { + for (var element in deck.energyList) { quantity += int.parse(element.quantity); } @@ -45,7 +45,7 @@ bool isValidDeckString(String deckString) { return true; } return false; - } catch(_) { + } catch (_) { return false; } -} \ No newline at end of file +} diff --git a/packages/deck_string_parser/lib/src/card.dart b/packages/deck_string_parser/lib/src/card.dart index 6689137..69664f4 100644 --- a/packages/deck_string_parser/lib/src/card.dart +++ b/packages/deck_string_parser/lib/src/card.dart @@ -13,4 +13,4 @@ class Card { void setQuantity(String quantity) { this.quantity = quantity; } -} \ No newline at end of file +} diff --git a/packages/deck_string_parser/lib/src/deck.dart b/packages/deck_string_parser/lib/src/deck.dart index 84ebb87..8bf09fe 100644 --- a/packages/deck_string_parser/lib/src/deck.dart +++ b/packages/deck_string_parser/lib/src/deck.dart @@ -13,4 +13,4 @@ class Deck { required this.trainerList, required this.energyList, }); -} \ No newline at end of file +} diff --git a/packages/deck_string_parser/lib/src/energy.dart b/packages/deck_string_parser/lib/src/energy.dart index becb5c1..d178d90 100644 --- a/packages/deck_string_parser/lib/src/energy.dart +++ b/packages/deck_string_parser/lib/src/energy.dart @@ -1,6 +1,6 @@ import 'package:deck_string_parser/src/card.dart'; -class Energy extends Card{ +class Energy extends Card { /// This represents Energy-cards /// /// `name` represents the name (and set, if applicable) of the card. @@ -8,4 +8,4 @@ class Energy extends Card{ required quantity, required name, }) : super(quantity: quantity, name: name); -} \ No newline at end of file +} diff --git a/packages/deck_string_parser/lib/src/parser.dart b/packages/deck_string_parser/lib/src/parser.dart index 7ed5c89..a688d3e 100644 --- a/packages/deck_string_parser/lib/src/parser.dart +++ b/packages/deck_string_parser/lib/src/parser.dart @@ -32,18 +32,20 @@ List> crudeSplit(String deck) { // Sanity check if (breaks.length < 2 || breaks.length > 3) { - throw FormatException("Expected either 3 or 4 paragraphs. Received ${breaks.length + 1}."); + throw FormatException( + "Expected either 3 or 4 paragraphs. Received ${breaks.length + 1}."); } // Split into a list of list, based on section List> buffer = []; - buffer.add(lines.sublist(0, breaks[0])); // Pokemon section + buffer.add(lines.sublist(0, breaks[0])); // Pokemon section // +1 to index to skip the empty line: - buffer.add(lines.sublist(breaks[0] + 1, breaks[1])); // Trainer section + buffer.add(lines.sublist(breaks[0] + 1, breaks[1])); // Trainer section // Energy section: if (breaks.length < 3) { buffer.add(lines.sublist(breaks[1] + 1)); - } else { // Strip off Total Cards section + } else { + // Strip off Total Cards section buffer.add(lines.sublist(breaks[1] + 1, breaks[2])); } @@ -56,7 +58,8 @@ List> stripHeaders(List> crudeDeckList) { // Check if headers exist var firstWord = crudeDeckList[0][0].split(" ")[0]; - if (isNumeric(firstWord)) { // LimitlessTCG format; no headers + if (isNumeric(firstWord)) { + // LimitlessTCG format; no headers return crudeDeckList; } @@ -103,11 +106,12 @@ List parsePokemonCards(List> crudeDeckList) { for (var line in section) { var words = line.split(" "); if (!isNumeric(words.last)) { - words.removeLast(); // strip holo card info (PTCGL format) + words.removeLast(); // strip holo card info (PTCGL format) } var indexOfSetAbbreviation = words.length - 2; var name = words.sublist(1, indexOfSetAbbreviation).join(" "); - var existingPokemonIndex = findExistingPokemon(name, words[indexOfSetAbbreviation], buffer); + var existingPokemonIndex = + findExistingPokemon(name, words[indexOfSetAbbreviation], buffer); if (existingPokemonIndex == -1) { buffer.add(Pokemon( @@ -117,7 +121,8 @@ List parsePokemonCards(List> crudeDeckList) { )); } else { var existingQuantity = buffer[existingPokemonIndex].quantity; - buffer[existingPokemonIndex].quantity = addQuantities(existingQuantity, words[0]); + buffer[existingPokemonIndex].quantity = + addQuantities(existingQuantity, words[0]); } } return buffer; @@ -169,15 +174,16 @@ List parseTrainerCards(List> crudeDeckList) { var existingTrainerIndex = findExistingTrainer(name, buffer); if (existingTrainerIndex == -1) { - buffer.add(Trainer( // No cards with the same name + buffer.add(Trainer( + // No cards with the same name quantity: words[0], name: name, )); - } else { // Merge with existing entry + } else { + // Merge with existing entry var existingQuantity = buffer[existingTrainerIndex].quantity; - buffer[existingTrainerIndex].setQuantity( - addQuantities(existingQuantity, words[0]) - ); + buffer[existingTrainerIndex] + .setQuantity(addQuantities(existingQuantity, words[0])); } } return buffer; @@ -202,15 +208,16 @@ List parseEnergyCards(List> crudeDeckList) { var existingEnergyIndex = findExistingEnergy(name, buffer); if (existingEnergyIndex == -1) { - buffer.add(Energy( // No cards with the same name + buffer.add(Energy( + // No cards with the same name quantity: words[0], name: name, )); - } else { // Merge with existing entry + } else { + // Merge with existing entry var existingQuantity = buffer[existingEnergyIndex].quantity; - buffer[existingEnergyIndex].setQuantity( - addQuantities(existingQuantity, words[0]) - ); + buffer[existingEnergyIndex] + .setQuantity(addQuantities(existingQuantity, words[0])); } } return buffer; @@ -223,14 +230,14 @@ List parseEnergyCards(List> crudeDeckList) { /// "{W} Energy Energy" -> "Water Energy" and so on. String ptcglEnergyFormatToEnglish(String energyName) { final Map energySymbols = { - "{D}" : "Dark", - "{F}" : "Fighting", - "{G}" : "Grass", - "{L}" : "Lightning", - "{M}" : "Metal", - "{W}" : "Water", - "{R}" : "Fire", - "{P}" : "Psychic", + "{D}": "Dark", + "{F}": "Fighting", + "{G}": "Grass", + "{L}": "Lightning", + "{M}": "Metal", + "{W}": "Water", + "{R}": "Fire", + "{P}": "Psychic", }; energySymbols.forEach((key, value) { energyName = energyName.replaceFirst(key, value); @@ -238,7 +245,7 @@ String ptcglEnergyFormatToEnglish(String energyName) { energyName = energyName.replaceFirst("Energy Energy", "Energy"); // Remove anything after Energy word var endToken = "Energy"; - var indexToEnd = energyName.indexOf(endToken)+endToken.length; + var indexToEnd = energyName.indexOf(endToken) + endToken.length; var cleanEnergyName = energyName.substring(0, indexToEnd); return cleanEnergyName; -} \ No newline at end of file +} diff --git a/packages/deck_string_parser/lib/src/pokemon.dart b/packages/deck_string_parser/lib/src/pokemon.dart index be6fb15..016ea22 100644 --- a/packages/deck_string_parser/lib/src/pokemon.dart +++ b/packages/deck_string_parser/lib/src/pokemon.dart @@ -7,4 +7,4 @@ class Pokemon extends Card { required name, required set, }) : super(quantity: quantity, name: name, set: set); -} \ No newline at end of file +} diff --git a/packages/deck_string_parser/lib/src/set_abbreviations.dart b/packages/deck_string_parser/lib/src/set_abbreviations.dart index 377d370..f5703fb 100644 --- a/packages/deck_string_parser/lib/src/set_abbreviations.dart +++ b/packages/deck_string_parser/lib/src/set_abbreviations.dart @@ -1,155 +1,305 @@ // Generated using Forecast on Saturday, 11. November 2023 08:43PM UTC // See: https://github.com/KOOKIIEStudios/Forecast const Set setAbbreviations = { - "BS", // Base Set - "JU", // Jungle - "FO", // Fossil - "B2", // Base Set 2 - "TR", // Team Rocket - "G1", // Gym Heroes - "G2", // Gym Challenge - "N1", // Neo Genesis - "N2", // Neo Discovery - "N3", // Neo Revelation - "N4", // Neo Destiny - "LC", // Legendary Collection - "EX", // Expedition Base Set - "AQ", // Aquapolis - "SK", // Skyridge - "RS", // EX Ruby & Sapphire - "SS", // EX Sandstorm - "DR", // EX Dragon - "MA", // EX Team Magma vs Team Aqua - "HL", // EX Hidden Legends - "FL", // EX FireRed & LeafGreen - "TRR", // EX Team Rocket Returns - "DX", // EX Deoxys - "EM", // EX Emerald - "UF", // EX Unseen Forces - "DS", // EX Delta Species - "LM", // EX Legend Maker - "HP", // EX Holon Phantoms - "CG", // EX Crystal Guardians - "DF", // EX Dragon Frontiers - "PK", // EX Power Keepers - "DP", // Diamond & Pearl - "MT", // Diamond & Pearl\u2014Mysterious Treasures - "SW", // Diamond & Pearl\u2014Secret Wonders - "GE", // Diamond & Pearl\u2014Great Encounters - "MD", // Diamond & Pearl\u2014Majestic Dawn - "LA", // Diamond & Pearl\u2014Legends Awakened - "SF", // Diamond & Pearl\u2014Stormfront - "PL", // Platinum - "RR", // Platinum\u2014Rising Rivals - "SV", // Platinum\u2014Supreme Victors - "AR", // Platinum\u2014Arceus - "HS", // HeartGold & SoulSilver - "UL", // HS\u2014Unleashed - "UD", // HS\u2014Undaunted - "TM", // HS\u2014Triumphant + "BS", + // Base Set + "JU", + // Jungle + "FO", + // Fossil + "B2", + // Base Set 2 + "TR", + // Team Rocket + "G1", + // Gym Heroes + "G2", + // Gym Challenge + "N1", + // Neo Genesis + "N2", + // Neo Discovery + "N3", + // Neo Revelation + "N4", + // Neo Destiny + "LC", + // Legendary Collection + "EX", + // Expedition Base Set + "AQ", + // Aquapolis + "SK", + // Skyridge + "RS", + // EX Ruby & Sapphire + "SS", + // EX Sandstorm + "DR", + // EX Dragon + "MA", + // EX Team Magma vs Team Aqua + "HL", + // EX Hidden Legends + "FL", + // EX FireRed & LeafGreen + "TRR", + // EX Team Rocket Returns + "DX", + // EX Deoxys + "EM", + // EX Emerald + "UF", + // EX Unseen Forces + "DS", + // EX Delta Species + "LM", + // EX Legend Maker + "HP", + // EX Holon Phantoms + "CG", + // EX Crystal Guardians + "DF", + // EX Dragon Frontiers + "PK", + // EX Power Keepers + "DP", + // Diamond & Pearl + "MT", + // Diamond & Pearl\u2014Mysterious Treasures + "SW", + // Diamond & Pearl\u2014Secret Wonders + "GE", + // Diamond & Pearl\u2014Great Encounters + "MD", + // Diamond & Pearl\u2014Majestic Dawn + "LA", + // Diamond & Pearl\u2014Legends Awakened + "SF", + // Diamond & Pearl\u2014Stormfront + "PL", + // Platinum + "RR", + // Platinum\u2014Rising Rivals + "SV", + // Platinum\u2014Supreme Victors + "AR", + // Platinum\u2014Arceus + "HS", + // HeartGold & SoulSilver + "UL", + // HS\u2014Unleashed + "UD", + // HS\u2014Undaunted + "TM", + // HS\u2014Triumphant // "CL", // Pok\u00e9mon TCG: Call of Legends - Clashes with the 2023 Pokémon Trading Card Game Classic - "BLW", // Black & White - "EPO", // Black & White\u2014Emerging Powers - "NVI", // Black & White\u2014Noble Victories - "NXD", // Black & White\u2014Next Destinies - "DEX", // Black & White\u2014Dark Explorers - "DRX", // Black & White\u2014Dragons Exalted - "DRV", // Dragon Vault - "BCR", // Black & White\u2014Boundaries Crossed - "PLS", // Black & White\u2014Plasma Storm - "PLF", // Black & White\u2014Plasma Freeze - "PLB", // Black & White\u2014Plasma Blast - "LTR", // Black & White\u2014Legendary Treasures - "KSS", // XY\u2014Kalos Starter Set - "XY", // XY - "FLF", // XY\u2014Flashfire - "FFI", // XY\u2014Furious Fists - "PHF", // XY\u2014Phantom Forces - "PRC", // XY\u2014Primal Clash - "DCR", // Double Crisis - "ROS", // XY\u2014Roaring Skies - "AOR", // XY\u2014Ancient Origins - "BKT", // XY\u2014BREAKthrough - "BKP", // XY\u2014BREAKpoint - "GEN", // Generations - "FCO", // XY\u2014Fates Collide - "STS", // XY\u2014Steam Siege - "EVO", // XY\u2014Evolutions - "SUM", // Sun & Moon - "GRI", // Sun & Moon\u2014Guardians Rising - "BUS", // Sun & Moon\u2014Burning Shadows - "SLG", // Shining Legends - "CIN", // Sun & Moon\u2014Crimson Invasion - "UPR", // Sun & Moon\u2014Ultra Prism - "FLI", // Sun & Moon\u2014Forbidden Light - "CES", // Sun & Moon\u2014Celestial Storm - "DRM", // Dragon Majesty - "LOT", // Sun & Moon\u2014Lost Thunder - "TEU", // Sun & Moon\u2014Team Up - "DET", // Detective Pikachu - "UNB", // Sun & Moon\u2014Unbroken Bonds - "UNM", // Sun & Moon\u2014Unified Minds - "HIF", // Hidden Fates - "CEC", // Sun & Moon\u2014Cosmic Eclipse - "SSH", // Sword & Shield - "RCL", // Sword & Shield\u2014Rebel Clash - "DAA", // Sword & Shield\u2014Darkness Ablaze - "CPA", // Champion's Path - "VIV", // Sword & Shield\u2014Vivid Voltage - "SHF", // Shining Fates - "BST", // Sword & Shield\u2014Battle Styles - "CRE", // Sword & Shield\u2014Chilling Reign - "EVS", // Sword & Shield\u2014Evolving Skies - "CEL", // Celebrations - "FST", // Sword & Shield\u2014Fusion Strike - "BRS", // Sword & Shield\u2014Brilliant Stars - "ASR", // Sword & Shield\u2014Astral Radiance - "PGO", // Pok\u00e9mon TCG: Pok\u00e9mon GO - "LOR", // Sword & Shield\u2014Lost Origin - "SIT", // Sword & Shield\u2014Silver Tempest - "CRZ", // Crown Zenith - "SVI", // Scarlet & Violet - "PAL", // Scarlet & Violet\u2014Paldea Evolved - "OBF", // Scarlet & Violet\u2014Obsidian Flames - "MEW", // Scarlet & Violet\u2014151 - "PAR", // Scarlet & Violet\u2014Paradox Rift - "SVE", // SVE Basic Energies - "WP", // Wizards Black Star Promos - "NP", // Nintendo Black Star Promos - "DPP", // DP Black Star Promos - "HSP", // HGSS Black Star Promos - "BWP", // BW Black Star Promos - "XYP", // XY Black Star Promos - "SMP", // SM Black Star Promos - "SSP", // SWSH Black Star Promos - "SVP", // SVP Black Star Promos - "MCD11", // McDonald's Collection 2011 - "MCD12", // McDonald's Collection 2012 - "MCD13", // McDonald's Collection 2013 - "MCD14", // McDonald's Collection 2014 - "MCD15", // McDonald's Collection 2015 - "MCD16", // McDonald's Collection 2016 - "MCD17", // McDonald's Collection 2017 - "MCD18", // McDonald's Collection 2018 - "MCD19", // McDonald's Collection 2019 - "MCD21", // McDonald's Collection 2021 - "MCD22", // McDonald's Collection 2022 - "M23", // McDonald's Collection 2023 - "POP1", // POP Series 1 - "POP2", // POP Series 2 - "POP3", // POP Series 3 - "POP4", // POP Series 4 - "POP5", // POP Series 5 - "POP6", // POP Series 6 - "POP7", // POP Series 7 - "POP8", // POP Series 8 - "POP9", // POP Series 9 - "PPS1", // Play! Pok\u00e9mon Prize Pack Series One - "PPS2", // Play! Pok\u00e9mon Prize Pack Series Two - "PPS3", // Play! Pok\u00e9mon Prize Pack Series Three - "SI", // Southern Islands - "CC", // Pok\u00e9 Card Creator Pack - "RM", // Pok\u00e9mon Rumble - "FUT20", // Pok\u00e9mon Futsal - "CL", // Pok\u00e9mon Trading Card Game Classic + "BLW", + // Black & White + "EPO", + // Black & White\u2014Emerging Powers + "NVI", + // Black & White\u2014Noble Victories + "NXD", + // Black & White\u2014Next Destinies + "DEX", + // Black & White\u2014Dark Explorers + "DRX", + // Black & White\u2014Dragons Exalted + "DRV", + // Dragon Vault + "BCR", + // Black & White\u2014Boundaries Crossed + "PLS", + // Black & White\u2014Plasma Storm + "PLF", + // Black & White\u2014Plasma Freeze + "PLB", + // Black & White\u2014Plasma Blast + "LTR", + // Black & White\u2014Legendary Treasures + "KSS", + // XY\u2014Kalos Starter Set + "XY", + // XY + "FLF", + // XY\u2014Flashfire + "FFI", + // XY\u2014Furious Fists + "PHF", + // XY\u2014Phantom Forces + "PRC", + // XY\u2014Primal Clash + "DCR", + // Double Crisis + "ROS", + // XY\u2014Roaring Skies + "AOR", + // XY\u2014Ancient Origins + "BKT", + // XY\u2014BREAKthrough + "BKP", + // XY\u2014BREAKpoint + "GEN", + // Generations + "FCO", + // XY\u2014Fates Collide + "STS", + // XY\u2014Steam Siege + "EVO", + // XY\u2014Evolutions + "SUM", + // Sun & Moon + "GRI", + // Sun & Moon\u2014Guardians Rising + "BUS", + // Sun & Moon\u2014Burning Shadows + "SLG", + // Shining Legends + "CIN", + // Sun & Moon\u2014Crimson Invasion + "UPR", + // Sun & Moon\u2014Ultra Prism + "FLI", + // Sun & Moon\u2014Forbidden Light + "CES", + // Sun & Moon\u2014Celestial Storm + "DRM", + // Dragon Majesty + "LOT", + // Sun & Moon\u2014Lost Thunder + "TEU", + // Sun & Moon\u2014Team Up + "DET", + // Detective Pikachu + "UNB", + // Sun & Moon\u2014Unbroken Bonds + "UNM", + // Sun & Moon\u2014Unified Minds + "HIF", + // Hidden Fates + "CEC", + // Sun & Moon\u2014Cosmic Eclipse + "SSH", + // Sword & Shield + "RCL", + // Sword & Shield\u2014Rebel Clash + "DAA", + // Sword & Shield\u2014Darkness Ablaze + "CPA", + // Champion's Path + "VIV", + // Sword & Shield\u2014Vivid Voltage + "SHF", + // Shining Fates + "BST", + // Sword & Shield\u2014Battle Styles + "CRE", + // Sword & Shield\u2014Chilling Reign + "EVS", + // Sword & Shield\u2014Evolving Skies + "CEL", + // Celebrations + "FST", + // Sword & Shield\u2014Fusion Strike + "BRS", + // Sword & Shield\u2014Brilliant Stars + "ASR", + // Sword & Shield\u2014Astral Radiance + "PGO", + // Pok\u00e9mon TCG: Pok\u00e9mon GO + "LOR", + // Sword & Shield\u2014Lost Origin + "SIT", + // Sword & Shield\u2014Silver Tempest + "CRZ", + // Crown Zenith + "SVI", + // Scarlet & Violet + "PAL", + // Scarlet & Violet\u2014Paldea Evolved + "OBF", + // Scarlet & Violet\u2014Obsidian Flames + "MEW", + // Scarlet & Violet\u2014151 + "PAR", + // Scarlet & Violet\u2014Paradox Rift + "SVE", + // SVE Basic Energies + "WP", + // Wizards Black Star Promos + "NP", + // Nintendo Black Star Promos + "DPP", + // DP Black Star Promos + "HSP", + // HGSS Black Star Promos + "BWP", + // BW Black Star Promos + "XYP", + // XY Black Star Promos + "SMP", + // SM Black Star Promos + "SSP", + // SWSH Black Star Promos + "SVP", + // SVP Black Star Promos + "MCD11", + // McDonald's Collection 2011 + "MCD12", + // McDonald's Collection 2012 + "MCD13", + // McDonald's Collection 2013 + "MCD14", + // McDonald's Collection 2014 + "MCD15", + // McDonald's Collection 2015 + "MCD16", + // McDonald's Collection 2016 + "MCD17", + // McDonald's Collection 2017 + "MCD18", + // McDonald's Collection 2018 + "MCD19", + // McDonald's Collection 2019 + "MCD21", + // McDonald's Collection 2021 + "MCD22", + // McDonald's Collection 2022 + "M23", + // McDonald's Collection 2023 + "POP1", + // POP Series 1 + "POP2", + // POP Series 2 + "POP3", + // POP Series 3 + "POP4", + // POP Series 4 + "POP5", + // POP Series 5 + "POP6", + // POP Series 6 + "POP7", + // POP Series 7 + "POP8", + // POP Series 8 + "POP9", + // POP Series 9 + "PPS1", + // Play! Pok\u00e9mon Prize Pack Series One + "PPS2", + // Play! Pok\u00e9mon Prize Pack Series Two + "PPS3", + // Play! Pok\u00e9mon Prize Pack Series Three + "SI", + // Southern Islands + "CC", + // Pok\u00e9 Card Creator Pack + "RM", + // Pok\u00e9mon Rumble + "FUT20", + // Pok\u00e9mon Futsal + "CL", + // Pok\u00e9mon Trading Card Game Classic }; diff --git a/packages/deck_string_parser/lib/src/trainer.dart b/packages/deck_string_parser/lib/src/trainer.dart index d2da935..03adc70 100644 --- a/packages/deck_string_parser/lib/src/trainer.dart +++ b/packages/deck_string_parser/lib/src/trainer.dart @@ -6,4 +6,4 @@ class Trainer extends Card { required quantity, required name, }) : super(quantity: quantity, name: name); -} \ No newline at end of file +} diff --git a/packages/deck_string_parser/test/deck_string_parser_test.dart b/packages/deck_string_parser/test/deck_string_parser_test.dart index 528ab30..6eab0f1 100644 --- a/packages/deck_string_parser/test/deck_string_parser_test.dart +++ b/packages/deck_string_parser/test/deck_string_parser_test.dart @@ -105,7 +105,6 @@ Total Cards: 60"""; var limitlessDeck = parseDeck(limitlessTcgDeckString); group('Validation checks', () { - test('`isValidDeckCount`: check if 60 cards in deck in ptcgoDeck', () { expect(isValidDeckCount(ptcgoDeck), true); }); diff --git a/packages/deck_string_parser/test/parser_test.dart b/packages/deck_string_parser/test/parser_test.dart index c60d76b..86802d4 100644 --- a/packages/deck_string_parser/test/parser_test.dart +++ b/packages/deck_string_parser/test/parser_test.dart @@ -101,32 +101,42 @@ Total Cards: 60"""; 11 Psychic Energy 5"""; - test('`crudeSplit` correctly splits a PTCGO deck string into 3 sections', () { + test('`crudeSplit` correctly splits a PTCGO deck string into 3 sections', + () { expect(crudeSplit(ptcgoDeckString).length, 3); }); - test('`crudeSplit` correctly splits a PTCGL deck string into 3 sections', () { + test('`crudeSplit` correctly splits a PTCGL deck string into 3 sections', + () { expect(crudeSplit(ptcglDeckString).length, 3); }); // Limitless TCG Deck Strings - test('`crudeSplit` correctly splits a LimitlessTCG deck string into 3 sections', () { + test( + '`crudeSplit` correctly splits a LimitlessTCG deck string into 3 sections', + () { expect(crudeSplit(limitlessTcgDeckString).length, 3); }); - test('`crudeSplit` extracts the correct number of pokemon for LimitlessTCG', () { + test('`crudeSplit` extracts the correct number of pokemon for LimitlessTCG', + () { expect(crudeSplit(limitlessTcgDeckString)[0].length, 13); }); - test('`crudeSplit` extracts the correct number of trainers for LimitlessTCG', () { + test( + '`crudeSplit` extracts the correct number of trainers for LimitlessTCG', + () { expect(crudeSplit(limitlessTcgDeckString)[1].length, 16); }); - test('`crudeSplit` extracts the correct number of energies for LimitlessTCG', () { + test( + '`crudeSplit` extracts the correct number of energies for LimitlessTCG', + () { expect(crudeSplit(limitlessTcgDeckString)[2].length, 1); }); - test('`crudeSplit` extracts the exact pokemon deckList for LimitlessTCG', () { + test('`crudeSplit` extracts the exact pokemon deckList for LimitlessTCG', + () { var expectedDeckList = [ [ "4 Ralts ASR 60", @@ -161,9 +171,7 @@ Total Cards: 60"""; "1 Sky Seal Stone CRZ 143", "2 Temple of Sinnoh ASR 155", ], - [ - "11 Psychic Energy 5" - ], + ["11 Psychic Energy 5"], ]; expect(crudeSplit(limitlessTcgDeckString), expectedDeckList); }); @@ -234,15 +242,21 @@ Total Cards: 60"""; [""], ]; - test('`parsePokemonCards` extracts the correct number of lines from the crude deck list', () { + test( + '`parsePokemonCards` extracts the correct number of lines from the crude deck list', + () { expect(parsePokemonCards(deck).length, 8); }); - test('`parseTrainerCards` extracts the correct number of lines from the crude deck list', () { + test( + '`parseTrainerCards` extracts the correct number of lines from the crude deck list', + () { expect(parseTrainerCards(deck).length, 15); }); - test('`parseTrainerCards` extracts the first card correctly as a Trainer object', () { + test( + '`parseTrainerCards` extracts the first card correctly as a Trainer object', + () { expect(parseTrainerCards(deck)[0].quantity, "3"); expect(parseTrainerCards(deck)[0].name, "Judge"); }); @@ -256,11 +270,15 @@ Total Cards: 60"""; expect(trainers.length, 2); }); - test('`parseEnergyCards` extracts the correct number of lines from the crude deck list', () { + test( + '`parseEnergyCards` extracts the correct number of lines from the crude deck list', + () { expect(parseEnergyCards(deck).length, 1); }); - test('`parsePokemonCards` extracts the first card correctly as a Pokemon object', () { + test( + '`parsePokemonCards` extracts the first card correctly as a Pokemon object', + () { expect(parsePokemonCards(deck)[0].quantity, "4"); expect(parsePokemonCards(deck)[0].name, "Lechonk"); expect(parsePokemonCards(deck)[0].set, "SVI"); @@ -272,12 +290,13 @@ Total Cards: 60"""; expect(parsePokemonCards(deck)[4].set, "ASR"); }); - test('`parseEnergyCards` extracts the first card correctly as a Energy object', () { + test( + '`parseEnergyCards` extracts the first card correctly as a Energy object', + () { expect(parseEnergyCards(deck)[0].quantity, "4"); expect(parseEnergyCards(deck)[0].name, "V Guard Energy"); }); - // TODO: Add test involving delta energy }); } diff --git a/packages/pokemon_pdf_builder/CHANGELOG.md b/packages/pokemon_pdf_builder/CHANGELOG.md index 515321e..ba9e32d 100644 --- a/packages/pokemon_pdf_builder/CHANGELOG.md +++ b/packages/pokemon_pdf_builder/CHANGELOG.md @@ -1,33 +1,41 @@ ## 2.2.0 + - Add age division marking - Add unicode font for checkbox symbol ## 2.0.2 + - Add title and author metadata - - Experimental - API docs are poorly documented + - Experimental - API docs are poorly documented - Update dependencies ## 2.0.1 + - Tweak the coordinates for the `date` field in letter-sized documents ## 2.0.0 + - Make the library take in `pdf/widget`-style arguments - - e.g. `widget.Image` and `widget.font` instead of `ByteArray` and `Uint8List` - - This allows these widgets to instantiated at start-up and kept in memory, rather than being converted from bytes to objects every pdf build/save + - e.g. `widget.Image` and `widget.font` instead of `ByteArray` and `Uint8List` + - This allows these widgets to instantiated at start-up and kept in memory, rather than being converted from bytes + to objects every pdf build/save - Expose class constructors directly, instead of using a getter function - - Unnecessary use of getter + - Unnecessary use of getter ## 1.0.0 - MVP + - Able to handle Letter-sized PDF - Various bug fixes ## 0.0.2 + - PDF document generation functionality - - Uses types exposed in deck_string_parser - - Able to handle empty/missing fields in forms - - Able to handle A4-sized PDFs - - Able to return a hydrated document as `Uint8List` for live preview in CastFORM + - Uses types exposed in deck_string_parser + - Able to handle empty/missing fields in forms + - Able to handle A4-sized PDFs + - Able to return a hydrated document as `Uint8List` for live preview in CastFORM - Detailed example code provided ## 0.0.1 + - Initialise package with Dart defaults diff --git a/packages/pokemon_pdf_builder/README.md b/packages/pokemon_pdf_builder/README.md index 0620e61..222c467 100644 --- a/packages/pokemon_pdf_builder/README.md +++ b/packages/pokemon_pdf_builder/README.md @@ -2,12 +2,15 @@ This package houses the business logic for turning decks into pdf generated docu ## Features -This library wraps around the [pdf](https://pub.dev/packages/pdf) library, and makes use of its PDF-construction functionality. -This library takes various input data, and places them in the correct position on top of either A4- or Letter-sized registration form templates. +This library wraps around the [pdf](https://pub.dev/packages/pdf) library, and makes use of its PDF-construction +functionality. +This library takes various input data, and places them in the correct position on top of either A4- or Letter-sized +registration form templates. ## Usage -This package is fairly straightforward to use. Refer to `/example` folder for a working detailed examples that can be run. +This package is fairly straightforward to use. Refer to `/example` folder for a working detailed examples that can be +run. ```dart import 'package:deck_string_parser/deck_string_parser.dart'; diff --git a/packages/pokemon_pdf_builder/example/pokemon_pdf_example.dart b/packages/pokemon_pdf_builder/example/pokemon_pdf_example.dart index 6c2fa8d..83832f8 100644 --- a/packages/pokemon_pdf_builder/example/pokemon_pdf_example.dart +++ b/packages/pokemon_pdf_builder/example/pokemon_pdf_example.dart @@ -1,7 +1,8 @@ import 'dart:io'; import 'dart:typed_data'; -import 'package:pdf/widgets.dart' as pw; + import 'package:deck_string_parser/deck_string_parser.dart'; +import 'package:pdf/widgets.dart' as pw; import 'package:pokemon_pdf_builder/pokemon_pdf.dart'; final deckString = """Pok?mon: 9 @@ -61,8 +62,8 @@ Future main() async { // create text style with font final pw.TextStyle textStyle = pw.TextStyle(font: font, fontSize: 10.0); // prepare background template - final Uint8List formTemplateAsBytes = File("./lib/assets/pokemon_decklist_a4.webp") - .readAsBytesSync(); + final Uint8List formTemplateAsBytes = + File("./lib/assets/pokemon_decklist_a4.webp").readAsBytesSync(); final pw.Image formTemplate = pw.Image(pw.MemoryImage(formTemplateAsBytes)); // Instantiate the form handler @@ -72,15 +73,14 @@ Future main() async { name: "Ash Ketchum", playerId: "1234567890", dateOfBirth: "02/20/2002", - deck: parseDeck(deckString), // Extract information from the deck string + deck: parseDeck(deckString), // Extract information from the deck string ); - // Hydrate! - final pdf = pokemonDoc.build(); // note: .buildPdf() will call .save for you + final pdf = pokemonDoc.build(); // note: .buildPdf() will call .save for you final file = File("D:/GitHub/CastFORM/example.pdf"); await file.writeAsBytes(await pdf.save()); print("Done!"); -} \ No newline at end of file +} diff --git a/packages/pokemon_pdf_builder/example/pokemon_pdf_letter_example.dart b/packages/pokemon_pdf_builder/example/pokemon_pdf_letter_example.dart index e58b927..350ceb2 100644 --- a/packages/pokemon_pdf_builder/example/pokemon_pdf_letter_example.dart +++ b/packages/pokemon_pdf_builder/example/pokemon_pdf_letter_example.dart @@ -1,7 +1,8 @@ import 'dart:io'; import 'dart:typed_data'; -import 'package:pdf/widgets.dart' as pw; + import 'package:deck_string_parser/deck_string_parser.dart'; +import 'package:pdf/widgets.dart' as pw; import 'package:pokemon_pdf_builder/pokemon_pdf.dart'; final deckString = """Pok?mon: 9 @@ -61,8 +62,8 @@ Future main() async { // create text style with font final pw.TextStyle textStyle = pw.TextStyle(font: font, fontSize: 10.0); // prepare background template - final Uint8List formTemplateAsBytes = File("./lib/assets/pokemon_decklist_letter.webp") - .readAsBytesSync(); + final Uint8List formTemplateAsBytes = + File("./lib/assets/pokemon_decklist_letter.webp").readAsBytesSync(); final pw.Image formTemplate = pw.Image(pw.MemoryImage(formTemplateAsBytes)); // Instantiate the form handler @@ -72,14 +73,14 @@ Future main() async { name: "Ash Ketchum", playerId: "1234567890", dateOfBirth: "02/20/2002", - deck: parseDeck(deckString), // Extract information from the deck string + deck: parseDeck(deckString), // Extract information from the deck string ); // Hydrate! - final pdf = pokemonDoc.build(); // note: .buildPdf() will call .save for you + final pdf = pokemonDoc.build(); // note: .buildPdf() will call .save for you final file = File("D:/GitHub/CastFORM/example.pdf"); await file.writeAsBytes(await pdf.save()); print("Done!"); -} \ No newline at end of file +} diff --git a/packages/pokemon_pdf_builder/example/unfilled_form_example.pdf.dart b/packages/pokemon_pdf_builder/example/unfilled_form_example.pdf.dart index 9c9420d..eb14617 100644 --- a/packages/pokemon_pdf_builder/example/unfilled_form_example.pdf.dart +++ b/packages/pokemon_pdf_builder/example/unfilled_form_example.pdf.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'dart:typed_data'; + import 'package:pdf/widgets.dart' as pw; import 'package:pokemon_pdf_builder/pokemon_pdf.dart'; @@ -24,8 +25,8 @@ Future main() async { // create text style with font final pw.TextStyle textStyle = pw.TextStyle(font: font, fontSize: 10.0); // prepare background template - final Uint8List formTemplateAsBytes = File("./lib/assets/pokemon_decklist_a4.webp") - .readAsBytesSync(); + final Uint8List formTemplateAsBytes = + File("./lib/assets/pokemon_decklist_a4.webp").readAsBytesSync(); final pw.Image formTemplate = pw.Image(pw.MemoryImage(formTemplateAsBytes)); // Let's try leaving fields empty, and see what happens this time diff --git a/packages/pokemon_pdf_builder/lib/pokemon_pdf.dart b/packages/pokemon_pdf_builder/lib/pokemon_pdf.dart index 2dd7946..ce4d0fe 100644 --- a/packages/pokemon_pdf_builder/lib/pokemon_pdf.dart +++ b/packages/pokemon_pdf_builder/lib/pokemon_pdf.dart @@ -2,5 +2,5 @@ library pokemon_pdf; export 'src/a4_form.dart'; +export 'src/division.dart'; export 'src/letter_form.dart'; -export 'src/division.dart'; \ No newline at end of file diff --git a/packages/pokemon_pdf_builder/lib/src/a4_form.dart b/packages/pokemon_pdf_builder/lib/src/a4_form.dart index 4c286c8..31edc63 100644 --- a/packages/pokemon_pdf_builder/lib/src/a4_form.dart +++ b/packages/pokemon_pdf_builder/lib/src/a4_form.dart @@ -53,4 +53,4 @@ class A4Form extends GenericForm implements Size { }) { initCoordinates(); } -} \ No newline at end of file +} diff --git a/packages/pokemon_pdf_builder/lib/src/abstract_form.dart b/packages/pokemon_pdf_builder/lib/src/abstract_form.dart index ba34d82..267c7d4 100644 --- a/packages/pokemon_pdf_builder/lib/src/abstract_form.dart +++ b/packages/pokemon_pdf_builder/lib/src/abstract_form.dart @@ -1,23 +1,30 @@ import 'dart:typed_data'; + import 'package:pdf/widgets.dart' as pw; abstract class AbstractForm { // Deck String fields -------------------------------------------------------- List generatePokemonColumn(); + List generatePokemonQuantity(); + List generatePokemonSet(); List generateTrainersColumn(); + List generateTrainerQuantity(); List generateEnergiesColumn(); + List generateEnergyQuantity(); // Other fields -------------------------------------------------------------- pw.Widget generateDoB(); + pw.Widget generateDivisions(); // PDF generation ------------------------------------------------------------ pw.Document build(); - Future buildPdf(); // builds and saves -} \ No newline at end of file + + Future buildPdf(); // builds and saves +} diff --git a/packages/pokemon_pdf_builder/lib/src/division.dart b/packages/pokemon_pdf_builder/lib/src/division.dart index e0c2611..f9aa8d5 100644 --- a/packages/pokemon_pdf_builder/lib/src/division.dart +++ b/packages/pokemon_pdf_builder/lib/src/division.dart @@ -14,4 +14,4 @@ Division getDivision(int birthYear) { return Division.senior; } return Division.masters; -} \ No newline at end of file +} diff --git a/packages/pokemon_pdf_builder/lib/src/generic_form.dart b/packages/pokemon_pdf_builder/lib/src/generic_form.dart index 80de69a..789b9ee 100644 --- a/packages/pokemon_pdf_builder/lib/src/generic_form.dart +++ b/packages/pokemon_pdf_builder/lib/src/generic_form.dart @@ -1,7 +1,8 @@ import 'dart:typed_data'; + +import 'package:deck_string_parser/deck_string_parser.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; -import 'package:deck_string_parser/deck_string_parser.dart'; import 'package:pokemon_pdf_builder/src/abstract_form.dart'; import 'package:pokemon_pdf_builder/src/division.dart'; import 'package:pokemon_pdf_builder/src/utility.dart'; @@ -21,6 +22,7 @@ class GenericForm implements AbstractForm { final pw.TextStyle? unicodeTextStyle; late PdfPageFormat pageFormat; + // Page size-related coordinates ------------------------------------------------- late double docX; late double docY; @@ -77,7 +79,8 @@ class GenericForm implements AbstractForm { @override List generatePokemonQuantity() { var list = deck?.pokemonList ?? []; - return populateWithStrings(extractQuantity(list), docX, pokemonRowY, textStyle); + return populateWithStrings( + extractQuantity(list), docX, pokemonRowY, textStyle); } @override @@ -95,7 +98,8 @@ class GenericForm implements AbstractForm { @override List generateTrainerQuantity() { var list = deck?.trainerList ?? []; - return populateWithStrings(extractQuantity(list), docX, trainerRowY, textStyle); + return populateWithStrings( + extractQuantity(list), docX, trainerRowY, textStyle); } @override @@ -107,7 +111,8 @@ class GenericForm implements AbstractForm { @override List generateEnergyQuantity() { var list = deck?.energyList ?? []; - return populateWithStrings(extractQuantity(list), docX, energyRowY, textStyle); + return populateWithStrings( + extractQuantity(list), docX, energyRowY, textStyle); } (String, String, String) splitDoB() { @@ -199,9 +204,8 @@ class GenericForm implements AbstractForm { @override pw.Document build() { final pdf = pw.Document( - title: "Pokemon TCG Tournament Registration Sheet", - author: "Generated by CastFORM Registration Sheet generator" - ); + title: "Pokemon TCG Tournament Registration Sheet", + author: "Generated by CastFORM Registration Sheet generator"); if (formTemplate == null) { return pdf; } diff --git a/packages/pokemon_pdf_builder/lib/src/letter_form.dart b/packages/pokemon_pdf_builder/lib/src/letter_form.dart index b0b7d86..ac360fe 100644 --- a/packages/pokemon_pdf_builder/lib/src/letter_form.dart +++ b/packages/pokemon_pdf_builder/lib/src/letter_form.dart @@ -53,4 +53,4 @@ class LetterForm extends GenericForm implements Size { }) { initCoordinates(); } -} \ No newline at end of file +} diff --git a/packages/pokemon_pdf_builder/lib/src/size.dart b/packages/pokemon_pdf_builder/lib/src/size.dart index b2b7608..9ee2903 100644 --- a/packages/pokemon_pdf_builder/lib/src/size.dart +++ b/packages/pokemon_pdf_builder/lib/src/size.dart @@ -1,3 +1,3 @@ abstract class Size { void initCoordinates(); -} \ No newline at end of file +} diff --git a/packages/pokemon_pdf_builder/lib/src/utility.dart b/packages/pokemon_pdf_builder/lib/src/utility.dart index 3aa1ac7..46e25e9 100644 --- a/packages/pokemon_pdf_builder/lib/src/utility.dart +++ b/packages/pokemon_pdf_builder/lib/src/utility.dart @@ -1,5 +1,5 @@ -import 'package:pdf/widgets.dart' as pw; import 'package:deck_string_parser/deck_string_parser.dart'; +import 'package:pdf/widgets.dart' as pw; /// Convert list of card to list of card names List extractName(List cards) { @@ -18,18 +18,17 @@ List extractSet(List cards) { /// Creates a list of pdf widgets from a list of strings List populateWithStrings( - List cardAttributes, - double width, - double height, - pw.TextStyle? textStyle, - ) { + List cardAttributes, + double width, + double height, + pw.TextStyle? textStyle, +) { List buffer = [ pw.Container( - // Empty child to push the text down + // Empty child to push the text down width: width, - height: height - ), + height: height), ...cardAttributes.map((element) => pw.Text(element, style: textStyle)), ]; return buffer; -} \ No newline at end of file +} diff --git a/test/widget_test.dart b/test/widget_test.dart index 0ccd140..a756158 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -5,11 +5,10 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. +import 'package:castform/app.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:castform/app.dart'; - void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame.