import 'dart:io'; import 'package:intl/intl.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart'; import 'package:toml/toml.dart'; class DartboardData { DartboardData({ required this.fullName, required this.phoneNumber, required this.email, // required this.address, required this.imagePath, required this.dartboardTheme, required this.experiences, required this.miscList, }); factory DartboardData.fromToml(String tomlFilePath) { final tomlData = TomlDocument.loadSync(tomlFilePath).toMap(); final List exps = tomlData.entries .where((e) => e.value is List && (e.value as List).firstOrNull is Map) .map((MapEntry expsEntry) { final String subsection = tomlData['${expsEntry.key}_name'] as String? ?? ''; final exps = (expsEntry.value as List).map((e) => e as Map).toList(); return exps.map((exp) { final TomlLocalDate? start = exp['start'] as TomlLocalDate?; final TomlLocalDate? end = exp['end'] as TomlLocalDate?; return DartboardExperience( title: exp['title'] as String, subsection: subsection, range: DateRange( start: start == null ? null : DateTime(start.date.year, start.date.month), end: end == null ? null : DateTime(end.date.year, end.date.month), ), attributes: (exp['attributes'] as List).map((e) => DartboardText(content: e as String)).toList(), location: exp['location'] as String?, ); }).toList(); }) .expand((i) => i) .toList(); return DartboardData( fullName: tomlData['full_name'] as String, phoneNumber: tomlData['phone_number'] as String, email: tomlData['email'] as String, imagePath: tomlData['image'] as String, dartboardTheme: DartboardTheme.fromToml(tomlData), experiences: exps, miscList: [ DartboardList(subsection: 'Skills & Interest', content: 'Proficient in flutter and other'), ], ); } final String fullName; final String phoneNumber; final String email; // final String address; final String imagePath; final DartboardTheme dartboardTheme; final List experiences; final List miscList; Font get font => dartboardTheme.font; PdfColor get primaryColor => dartboardTheme.primaryColor; PdfColor get accentColor => dartboardTheme.accentColor; PdfColor get backgroundColor => dartboardTheme.backgroundColor; TextStyle get headerTextStyle => TextStyle(fontSize: 18, font: font, fontWeight: FontWeight.bold, color: const PdfColorGrey(0.18)); TextStyle get subheaderTextStyle => TextStyle(fontSize: 14, font: font, color: const PdfColorGrey(0.3)); TextStyle get defaultTextStyle => TextStyle(fontSize: 10, font: font, color: const PdfColorGrey(0.45)); Map> get groupedExperiences { final Map> exps = {}; for (final DartboardExperience exp in experiences) { if (!exps.containsKey(exp.subsection)) { exps[exp.subsection] = []; } exps[exp.subsection]!.add(exp); } return exps; } @override int get hashCode { return Object.hashAll([fullName, phoneNumber, email, imagePath, ...experiences]); } @override bool operator ==(Object other) { return super.hashCode == other.hashCode; } } class DartboardExperience { DartboardExperience({ required this.title, required this.attributes, required this.range, required this.subsection, this.location, }); final String subsection; final String title; String? location; final List attributes; final DateRange range; @override int get hashCode { return Object.hashAll([subsection, title, ...attributes, range]); } @override bool operator ==(Object other) { return super.hashCode == other.hashCode; } } class DateRange { DateRange({required this.start, required this.end}); final DateTime? start; final DateTime? end; @override String toString() { final DateFormat dateFormat = DateFormat("MMM yyyy"); if (start == null && end == null) { return ''; } if (start == null && end != null) { return dateFormat.format(end!); } if (start != null && end == null) { return "${dateFormat.format(start!)} - Current"; } return "${dateFormat.format(start!)} - ${dateFormat.format(end!)}"; } @override int get hashCode { return Object.hashAll([start, end]); } @override bool operator ==(Object other) { return super.hashCode == other.hashCode; } } class DartboardList { DartboardList({required this.subsection, required this.content}); final String subsection; final String content; @override int get hashCode { return Object.hashAll([subsection, content]); } @override bool operator ==(Object other) { return super.hashCode == other.hashCode; } } class DartboardTheme { DartboardTheme({ required this.primaryHex, required this.accentHex, required this.backgroundHex, required this.fontPath, required this.bulletPoint, }); factory DartboardTheme.fromToml(Map toml) => DartboardTheme( primaryHex: (toml['theme'] as Map)['primary_hex'] as String, accentHex: (toml['theme'] as Map)['accent_hex'] as String, backgroundHex: (toml['theme'] as Map)['background_hex'] as String, fontPath: (toml['theme'] as Map)['font'] as String, bulletPoint: (toml['theme'] as Map)['bullet_point'] as String? ?? '-', ); factory DartboardTheme.retro() => DartboardTheme( primaryHex: "00AA00", accentHex: "44EE66", backgroundHex: "FFFFFF", fontPath: "nerd.ttf", bulletPoint: '-', ); static const double inch = 72.0; static const double cm = inch / 2.54; static const double mm = inch / 25.4; static const width = 21.0 * cm; static const height = 29.7 * cm; static const margin = 2.0 * cm; final String primaryHex; final String accentHex; final String backgroundHex; final String fontPath; final String bulletPoint; Font? _font; PdfColor get primaryColor => PdfColor.fromHex(primaryHex); PdfColor get accentColor => PdfColor.fromHex(accentHex); PdfColor get backgroundColor => PdfColor.fromHex(backgroundHex); Font get font { _font ??= Font.ttf(File(fontPath).readAsBytesSync().buffer.asByteData()); return _font!; } @override int get hashCode { return Object.hashAll([primaryHex, accentHex, backgroundHex, fontPath, bulletPoint]); } @override bool operator ==(Object other) { return super.hashCode == other.hashCode; } } enum DartboardTextType { normal, linkText } typedef DartboardTextLinkData = ({String text, String? url, DartboardTextType type}); /// Automatically detects and parses strings with hypertext in a markdown format class DartboardText { DartboardText({required this.content}); final String content; static final _markdownLinkRegex = RegExp(r'\[(.*?)\]\((.*?)\)'); bool get hasLinkMarkup => _markdownLinkRegex.hasMatch(content); List toTextLinkList() { final markdownLinkRegex = RegExp(r'\[(.*?)\]\((.*?)\)'); if (markdownLinkRegex.hasMatch(content)) { final matches = markdownLinkRegex.allMatches(content).toList(); final List stringSections = []; int prevStartIndex = 0; while (matches.isNotEmpty) { final match = matches.removeAt(0); stringSections .add((text: content.substring(prevStartIndex, match.start), url: null, type: DartboardTextType.normal)); stringSections.add((text: match.group(1)!, url: match.group(2), type: DartboardTextType.linkText)); prevStartIndex = match.end; } stringSections .add((text: content.substring(prevStartIndex, content.length), url: null, type: DartboardTextType.normal)); return stringSections; } else { return [(text: content, url: null, type: DartboardTextType.normal)]; // return Text("${bulletString != null ? '$bulletString ' : ''}$content", style: style); } } @override int get hashCode { return Object.hashAll([content]); } @override bool operator ==(Object other) { return super.hashCode == other.hashCode; } } String _getSubsectionFromKey(String key) { switch (key) { case 'exp': } }