La clase de terceros FPDF
Estoy haciendo una aplicación web de facturación. Utilizo el dueto PHP y MySQL.
Este dueto se convirtio en un trio en el momento en que necesitaba imprimir las facturas y/o enviarlas por email. En este caso se me ocurrieron dos soluciones una, la mas sencilla, utilizar una libreria (PHP) de terceros para generar un PDF, y otra, currarme un código para imprimir desde el formulario HTML. Esta última descartada por tiempo y porque seguro habria que pelearse no solo con PHP sino con CSS.
El famoso google me devolvió la página http://www.fpdf.org/. Instalé esta libreria (solo es subirla al servidor). Seguidamente con ensayo y error y ayuda del manual en la misma web fui entendiendo el comportamiento.
La verdad es que es sencilla pero potente, me recuerda mucho a una experiencia con C# y sus clases para imprimir.
Cuando ya habia conseguido dominarla, saltó un problema y es que no se podian ver los caracteres castellanos como la "ñ, ó, ü" en el PDF generado (ver figura 1), pero sin embargo se veia bien en el formulario HTML (ver figura 2). Asi que volviendo a google encontre que se podia solucionar añadiendo
$txt = utf8_decode($txt);
a la funcion
function Cell($w, $h=0, $txt='',
$border=0, $ln=0, $align='', $fill=false, $link='')
{...}
de la clase fpdf.php.
Esto en principio funcionó, digo en principio porque los datos no los estaba recuperando desde la BD sino que los ponia directamente (tecleaba en el PDFFactura.php) en forma de simulacion de los mismos.
El siguiente proceso, recuperar los datos de la BD y con estos generar la factura, me llevaria a tener que estudiar sobre los juegos de caracteres (CHARSET), el cotejamiento (COLLATION) y como se comunicaba MySQL y la aplicacion.
Breve resumen:
El charset viene a ser el conjunto de simbolos de un lenguaje, es decir en español existe la "ñ" mientras que en ingles no ambos alfabetos son latinos, luego el CHARSET viene a ser LATIN y el cotejamiento es la forma de compararlos. Si por ejemplo hacemos una peticion "SELECT" con un order by "Nombre_empresa" entraria en juego el cotejamiento, suponiendo que las empresas "MCQ" y "mcq" existen y son distintas cual deberia ir primera en el result?. Esto depende si el cotejamiento es:
_ci: Case insensitive
_cs: Case sensitive
_bin: Binario
No he hecho pruebas sobre esto, pero la teoria es esa.
Volviendo a mi dilema...
En el formulario web se visualizaba bien porque estoy trabajando con la directiva:
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
y como mi MySQL tiene estos parametros:
charset server Latin1
collation server latin1_swedish_ci
no generaba conflicto entre esos dos juegos (latin1 e iso-8859-1).
La secuencia era esta:
La información de MySQL a PHP se pasa en Latin1, el PHP trabaja con UTF-8 Unicode, por eso lo recibe y los transforma a UTF-8 pero no se porque razón (especulo que el HTML usara una funcion analoga a utfdecode pero para Latin1) automaticamente se convertia en iso-8859-1 visualizandose perfectamente en la web. (ver figura 2)
Por lógica, debería suceder lo mismo en el archivo PHP del PDF (PDFFactura.php), pero no era asi. (ver figura 1) al parecer se quedaba en Latin1 y la clase fpdf.php lo pasaba a UTF-8 pero con los caracteres desconocidos. Como no se iba a visualizar en un HTML sino en PDF, la clase no hacia una conversion al iso-8859-1.
Leyendo este articulo. Deduje que tenia que cambiar la codificación en que me tenia que devolver la informacion MySQL.
La secuencia deberia ser esta:
De MySQL a PHP se pasa en UTF-8 (ya no en Latin1). Mi clase fpdf.php (en PHP) la recibe correctamente puesto que PHP trabaja con UTF-8 y cuando utilizo el metodo CELL de la clase mencionada, le aplico la funcion utf8_decode($txt); transformado asi al iso-8859-1. que es la codificacion que usa la libreria fpdf.php.
Obteniendo el resultado (ver imagen 3).
El código relacionado es el siguiente:
Nombre archivo: PDFFactura.php
<?php include ($_SERVER['DOCUMENT_ROOT'].'/proyMODULOS/config.php'); ?>
<?php
require_once $_RUTARAIZ.'Clases/fpdf.php';//http://www.fpdf.org/
?>
<?php
session_start();
$oUsuSesion=$_SESSION['oUsuSesion'];
if($oUsuSesion==null||!$oUsuSesion->getSesion())
{
$_SESSION['sWarning']="Debe iniciar sesión.";
header('Location: ../../index.php');
exit;
}
else
{
//Me conecto
$oBD = new CBaseDatos();
$bEstado=$oBD->fBoolConectar();
//Cambio la codificacion en que MySQL me enviara la informacion
//este archivo.php ya no lo recibira en LATIN1
mysql_query("SET NAMES utf8");
//Recupero los datos
//La factura y sus Detalles, el Cliente
$oFactura = $_SESSION['oFactura'];
$oFactura->pAtributosDesdeIDN($oBD);
//Este constructor se puede mejorar con variables por defecto!!
$oCliente=new CCliente($oFactura->getIdCliente(), null, null, null, null, null, null, null, null, null, null, null, null);
$oCliente->pAtributosDesdeIDN($oBD);
//Creo mi array con los detalles de la factura
$arDetalles=CDetalleFactura::fArTabla($oFactura->getIdFactura(), $oBD);
$sWarning=$_SESSION['sWarning'];
}
//Heredo de la clase FPDF
class PDF extends FPDF
{
//http://www.fpdf.org/
//Cabecera de página, Titulo y Logo
function Header()
{
//Arial bold 15
$this->SetFont('Arial','B',15);
//Logo de la empresa
$this->Image('../Images/LogoHoja.jpg',137,1,74);
//Movernos a la derecha
$this->Cell(1);
//Título
$this->Cell(10,10,'Factura nº: ',0,0,'L');
}
//Pie de página
function Footer()
{
//Posición: a 1,5 cm del final
$this->SetY(-15);
//Arial italic 8
$this->SetFont('Arial','B',8);
//Número de página
$this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C');
}
//Muestra el Nº al lado de "FACTURA nº:"
function NFactura(CFactura $oFactura)
{
//Arial bold 15
$this->SetFont('Arial','B',15);
$this->Cell(20);
//Título
$this->Cell(10,10,$oFactura->getIdFactura(),0,0,'L');
//Salto de línea
$this->Ln(20);
}
function MiEmpresa(CFactura $oFactura)
{
$dFechaLarga=CUtils::fFechaLarga($oFactura->getFecha());
$sEmpresa="El nombre de tu empresa"; //La empresa que emitira la factura
$sNIF="B5555555";
$x=115;
//Arial bold 15
$this->SetFont('Arial','B',12);
$this->Cell($x);
$this->Cell(1,7,'FECHA:',20,0,'L');
$this->Ln();
$this->SetFont('Arial','',12);
$this->Cell($x);
$this->Cell(1,7,$dFechaLarga,20,0,'L');
$this->Ln();
$this->Cell($x);
$this->Cell(1,6,$sEmpresa,20,0,'L');
$this->Ln();
$this->Cell($x);
$this->Cell(1,6,$sNIF,20,0,'L');
$this->Ln(20);
}
function Cliente(CCliente $oCliente)
{
$this->SetFont('Arial','BU',12);
$this->Cell(0,7,'FACTURAR A:',0,0,'L');
$this->Ln();
$this->SetFont('Arial','B',12);
$this->Cell(0,7,$oCliente->getEmpresa(),0,0,'L');
$this->Ln();
$this->SetFont('Arial','',12);
$this->Cell(0,7,$oCliente->getCifnif(),0,0,'L');
$this->Ln();
$this->Cell(0,7,$oCliente->getDireccion(),0,0,'L');
$this->Ln();
$this->Cell(0,7,$oCliente->getCodigoPostal().' - '.$oCliente->getCiudad(),0,0,'L');
$this->Ln(20);
}
function Detalles($arDetalles)
{
$header=array('Concepto','Cantidad');
$this->SetFont('Arial','B',12);
//Anchuras de las columnas
$w=array(150,35);
//Cabeceras
for($i=0;$iCell($w[$i],7,$header[$i],1,0,'C');
}
$this->Ln();
$this->SetFont('Arial','',12);
//Datos
foreach($arDetalles as $row)
{
$this->Cell($w[0],6,$row[CONCEPTO],'LR');
$this->Cell($w[1],6,number_format($row[CANTIDAD],2,',','').' '.chr(128),'LR',0,'R');
$this->Ln();
}
//Línea de cierre
$this->Cell(array_sum($w),0,'','T');
$this->Ln();
}
function Totales(CFactura $oFactura)
{
$this->SetFont('Arial','B',12);
$arTitulos[0]='Subtotal: ';
$arTitulos[1]='Iva: ';
$arTitulos[2]='Total: ';
//Anchuras de las columnas
$w=array(150,35);
//Subtotal
$this->Cell($w[0],6,$arTitulos[0],'LR',0,'R');
$this->Cell($w[1],6,number_format($oFactura->getSubtotal(),2,',','').' '.chr(128),'LR',0,'R');
$this->Ln();
//Iva
$this->Cell($w[0],6,$arTitulos[1],'LR',0,'R');
$this->Cell($w[1],6,number_format($oFactura->getIva(),2,',','').' '.chr(128),'LR',0,'R');
$this->Ln();
//Total
$this->Cell($w[0],6,$arTitulos[2],'LR',0,'R');
$this->Cell($w[1],6,number_format($oFactura->getTotal(),2,',','').' '.chr(128),'LR',0,'R');
$this->Ln();
//Línea de cierre
$this->Cell(array_sum($w),0,'','T');
}
}
//Creación del objeto de la clase heredada
$pdf=new PDF();
$pdf->Header($oFactura);
//Muestra numero de paginas en el pie
$pdf->AliasNbPages();
//Creo pagina sobre la que diburjare
$pdf->AddPage();
$pdf->NFactura($oFactura);
//Creo los datos de la empresa emisora
$pdf->MiEmpresa($oFactura);
$pdf->Ln(10);
//Creo el cliente
$pdf->Cliente($oCliente);
//Creo los detalles
$pdf->Detalles($arDetalles);
//Creo los totales
$pdf->Totales($oFactura);
$pdf->Output();
?>
Autor: Eduardo A. F.
Publicado: 13-08-2010 20:59
Actualizado: 06-11-2010 19:06