Files
hidalyx-drluiscastro/lib/features/referrals/services/doctor_referral_pdf_service.dart
T

263 lines
9.2 KiB
Dart

import 'dart:typed_data';
import 'package:flutter/services.dart' show rootBundle;
import 'package:intl/intl.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
class DoctorReferralPdfService {
/// Genera el documento PDF de Referimiento a Doctor (a) en formato Media Carta Vertical
/// (5.5 x 8.5 pulgadas / 396 x 612 pt).
static Future<Uint8List> buildDoctorReferralPdf({
required String doctorName,
required String specialty,
required String subspecialty,
required String generalContact,
required String referralText,
DateTime? date,
pw.Font? fontRegular,
pw.Font? fontBold,
pw.Font? fontTitle,
String? logoSvg,
}) async {
final pdf = pw.Document(title: 'Referir a Doctor (a)');
final formattedDate = DateFormat('dd/MM/yyyy').format(date ?? DateTime.now());
// Formato Carta Vertical (8.5 x 11.0 pulgadas / 612 x 792 pt):
// Todo el contenido abarca estrictamente la mitad superior de la hoja (5.5 pulgadas / 396 pt),
// dejando la mitad inferior libre para corte o papel pre-cortado.
const pageFormat = PdfPageFormat(
8.5 * PdfPageFormat.inch,
11.0 * PdfPageFormat.inch,
marginLeft: 28,
marginRight: 28,
marginTop: 16,
marginBottom: 16,
);
// Tipografías estándar con fallback a fuentes base PDF si no hay conexión a internet
pw.Font regular = fontRegular ?? pw.Font.helvetica();
pw.Font bold = fontBold ?? pw.Font.helveticaBold();
pw.Font titleFont = fontTitle ?? pw.Font.timesBoldItalic();
if (fontRegular == null || fontBold == null || fontTitle == null) {
try {
regular = await PdfGoogleFonts.robotoRegular();
bold = await PdfGoogleFonts.robotoBold();
titleFont = await PdfGoogleFonts.playfairDisplayItalic();
} catch (_) {
regular = pw.Font.helvetica();
bold = pw.Font.helveticaBold();
titleFont = pw.Font.timesBoldItalic();
}
}
String? svgContent = logoSvg;
if (svgContent == null) {
try {
svgContent = await rootBundle.loadString('assets/images/hidalyx_logo_blue.svg');
} catch (_) {
svgContent = null;
}
}
pdf.addPage(
pw.Page(
pageFormat: pageFormat,
build: (pw.Context context) {
return pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.stretch,
children: [
// Mitad superior de la hoja (máximo 5.5 pulgadas / 396 pt)
pw.ConstrainedBox(
constraints: const pw.BoxConstraints(
maxHeight: 5.5 * PdfPageFormat.inch - 32,
),
child: pw.Column(
mainAxisSize: pw.MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.stretch,
children: [
// 1. Cabecera con Nombre del Doctor y Badge Sutil de Hidalyx Med
pw.Stack(
children: [
pw.Align(
alignment: pw.Alignment.center,
child: pw.Column(
children: [
pw.Text(
doctorName.isNotEmpty ? doctorName : 'Dr. Luis A. Castro Sosa',
style: pw.TextStyle(
font: titleFont,
fontSize: 22,
fontWeight: pw.FontWeight.bold,
color: PdfColor.fromHex('1E293B'),
),
),
pw.SizedBox(height: 3),
if (specialty.isNotEmpty)
pw.Text(
specialty.toUpperCase(),
style: pw.TextStyle(
font: bold,
fontSize: 10.5,
letterSpacing: 0.5,
color: PdfColor.fromHex('0F172A'),
),
),
if (subspecialty.isNotEmpty) ...[
pw.SizedBox(height: 2),
pw.Text(
subspecialty.toUpperCase(),
textAlign: pw.TextAlign.center,
style: pw.TextStyle(
font: regular,
fontSize: 8.5,
color: PdfColor.fromHex('475569'),
),
),
],
pw.SizedBox(height: 3),
pw.Text(
'Fecha: $formattedDate',
style: pw.TextStyle(
font: regular,
fontSize: 9.5,
color: PdfColor.fromHex('334155'),
),
),
],
),
),
// Badge sutil de Hidalyx Med en la esquina superior derecha
pw.Positioned(
top: 0,
right: 0,
child: pw.Row(
mainAxisSize: pw.MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.center,
children: [
if (svgContent != null && svgContent.isNotEmpty) ...[
pw.Opacity(
opacity: 0.6,
child: pw.SvgImage(
svg: svgContent,
width: 7.5,
height: 7.5,
),
),
pw.SizedBox(width: 3),
],
pw.Text(
'hidalyx.com',
style: pw.TextStyle(
font: regular,
fontSize: 6.0,
color: PdfColor.fromHex('94A3B8'),
),
),
],
),
),
],
),
pw.SizedBox(height: 6),
// Línea divisoria decorativa del membrete
pw.Container(
height: 1,
color: PdfColor.fromHex('CBD5E1'),
),
pw.SizedBox(height: 8),
// 2. Contenido del Referimiento (Idéntico a AlfaMedical con fontSize 13-14)
pw.Container(
padding: const pw.EdgeInsets.symmetric(vertical: 6),
child: pw.Text(
referralText.trim().isNotEmpty
? referralText.trim()
: 'Sin detalles de referimiento.',
style: pw.TextStyle(
font: regular,
fontSize: 10.5,
lineSpacing: 3.5,
color: PdfColor.fromHex('1E293B'),
),
),
),
pw.SizedBox(height: 10),
// Línea divisoria decorativa previa al pie
pw.Container(
height: 0.5,
color: PdfColor.fromHex('CBD5E1'),
),
pw.SizedBox(height: 5),
// 3. Pie de página con Contacto del Consultorio
if (generalContact.isNotEmpty) ...[
pw.Container(
padding: const pw.EdgeInsets.only(top: 4),
child: pw.Text(
generalContact.toUpperCase(),
textAlign: pw.TextAlign.center,
style: pw.TextStyle(
font: regular,
fontSize: 6.8,
color: PdfColor.fromHex('64748B'),
lineSpacing: 1.5,
),
),
),
],
pw.SizedBox(height: 3),
// Confirmación institucional
pw.Center(
child: pw.Text(
'Documento médico emitido electrónicamente',
style: pw.TextStyle(
font: regular,
fontSize: 6.0,
color: PdfColor.fromHex('94A3B8'),
),
),
),
],
),
),
],
);
},
),
);
return pdf.save();
}
/// Envía a imprimir directamente o abre el diálogo nativo de impresión/PDF.
static Future<void> printDoctorReferral({
required String doctorName,
required String specialty,
required String subspecialty,
required String generalContact,
required String referralText,
DateTime? date,
}) async {
final pdfBytes = await buildDoctorReferralPdf(
doctorName: doctorName,
specialty: specialty,
subspecialty: subspecialty,
generalContact: generalContact,
referralText: referralText,
date: date,
);
await Printing.layoutPdf(
onLayout: (PdfPageFormat format) async => pdfBytes,
name: 'Referimiento_Doctor',
);
}
}