263 lines
9.2 KiB
Dart
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 DepartmentReferralPdfService {
|
|
/// Genera el documento PDF de Referimiento a Departamento en formato Media Carta Vertical
|
|
/// (5.5 x 8.5 pulgadas / 396 x 612 pt).
|
|
static Future<Uint8List> buildDepartmentReferralPdf({
|
|
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 Departamento');
|
|
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)
|
|
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> printDepartmentReferral({
|
|
required String doctorName,
|
|
required String specialty,
|
|
required String subspecialty,
|
|
required String generalContact,
|
|
required String referralText,
|
|
DateTime? date,
|
|
}) async {
|
|
final pdfBytes = await buildDepartmentReferralPdf(
|
|
doctorName: doctorName,
|
|
specialty: specialty,
|
|
subspecialty: subspecialty,
|
|
generalContact: generalContact,
|
|
referralText: referralText,
|
|
date: date,
|
|
);
|
|
|
|
await Printing.layoutPdf(
|
|
onLayout: (PdfPageFormat format) async => pdfBytes,
|
|
name: 'Referimiento_Departamento',
|
|
);
|
|
}
|
|
}
|